blob: 4c875014da04390a46e817f18833bec8bddd102e [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2 * Copyright 2011, Big Switch Networks, Inc.
3 * Originally created by David Erickson, Stanford University
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain
7 * a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 **/
17
Jonathan Hart284e70f2014-07-05 12:32:51 -070018package net.onrc.onos.core.linkdiscovery;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080019
20import java.io.IOException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080021import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.List;
28import java.util.Map;
29import java.util.Map.Entry;
30import java.util.Set;
Jonathan Hart284e70f2014-07-05 12:32:51 -070031import java.util.concurrent.CopyOnWriteArrayList;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080032import java.util.concurrent.ScheduledExecutorService;
33import java.util.concurrent.TimeUnit;
34import java.util.concurrent.locks.ReentrantReadWriteLock;
35
36import net.floodlightcontroller.core.FloodlightContext;
37import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080038import net.floodlightcontroller.core.IOFMessageListener;
39import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070040import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080041import net.floodlightcontroller.core.IOFSwitchListener;
Jonathan Hart284e70f2014-07-05 12:32:51 -070042import net.floodlightcontroller.core.IUpdate;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080043import net.floodlightcontroller.core.annotations.LogMessageCategory;
44import net.floodlightcontroller.core.annotations.LogMessageDoc;
45import net.floodlightcontroller.core.annotations.LogMessageDocs;
46import net.floodlightcontroller.core.module.FloodlightModuleContext;
47import net.floodlightcontroller.core.module.FloodlightModuleException;
48import net.floodlightcontroller.core.module.IFloodlightModule;
49import net.floodlightcontroller.core.module.IFloodlightService;
50import net.floodlightcontroller.core.util.SingletonTask;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080051import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080052import net.floodlightcontroller.threadpool.IThreadPoolService;
Jonathan Hart23701d12014-04-03 10:45:48 -070053import net.onrc.onos.core.linkdiscovery.web.LinkDiscoveryWebRoutable;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070054import net.onrc.onos.core.packet.Ethernet;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070055import net.onrc.onos.core.packet.LLDP;
Jonathan Hart299d1132014-06-27 09:25:28 -070056import net.onrc.onos.core.packet.OnosLldp;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070057import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hart299d1132014-06-27 09:25:28 -070058import net.onrc.onos.core.util.SwitchPort;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070059
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070060import org.projectfloodlight.openflow.protocol.OFFactories;
61import org.projectfloodlight.openflow.protocol.OFFactory;
62import org.projectfloodlight.openflow.protocol.OFMessage;
63import org.projectfloodlight.openflow.protocol.OFPacketIn;
64import org.projectfloodlight.openflow.protocol.OFPacketOut;
65import org.projectfloodlight.openflow.protocol.OFPortConfig;
66import org.projectfloodlight.openflow.protocol.OFPortDesc;
67import org.projectfloodlight.openflow.protocol.OFPortReason;
68import org.projectfloodlight.openflow.protocol.OFPortState;
69import org.projectfloodlight.openflow.protocol.OFPortStatus;
70import org.projectfloodlight.openflow.protocol.OFType;
71import org.projectfloodlight.openflow.protocol.OFVersion;
72import org.projectfloodlight.openflow.protocol.action.OFAction;
73import org.projectfloodlight.openflow.types.OFBufferId;
74import org.projectfloodlight.openflow.types.OFPort;
75import org.projectfloodlight.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080076import org.slf4j.Logger;
77import org.slf4j.LoggerFactory;
78
79/**
Jonathan Hart284e70f2014-07-05 12:32:51 -070080 * Discovers links between OpenFlow switches.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070081 * <p>
82 * Discovery is performed by sending probes (LLDP packets) over the links in the
83 * data plane. The LinkDiscoveryManager sends probes periodically on all ports
84 * on all connected switches. The probes contain the sending switch's DPID and
85 * outgoing port number. LLDP packets that are received (via an OpenFlow
86 * packet-in) indicate there is a link between the receiving port and the
87 * sending port, which was encoded in the LLDP. When the LinkDiscoveryManager
88 * observes a new link, a Link object is created and an event is fired for any
89 * event listeners.
90 * </p>
Jonathan Hart284e70f2014-07-05 12:32:51 -070091 * Links are removed for one of three reasons:
92 * <ul>
93 * <li>A probe has not been received on the link for an interval (the timeout
94 * interval)</li>
95 * <li>The port went down or was disabled (as observed by OpenFlow port-status
96 * messages) or the disconnection of the switch</li>
97 * <li>Link discovery was explicitly disabled on a port with the
98 * {@link #disableDiscoveryOnPort(long, short)} method</li>
99 * </ul>
100 * When the LinkDiscoveryManager removes a link it also fires an event for the
101 * listeners.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800102 */
103@LogMessageCategory("Network Topology")
Jonathan Hart284e70f2014-07-05 12:32:51 -0700104public class LinkDiscoveryManager implements IOFMessageListener, IOFSwitchListener,
Ray Milkey269ffb92014-04-03 14:43:30 -0700105 ILinkDiscoveryService, IFloodlightModule {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800106
Jonathan Hart284e70f2014-07-05 12:32:51 -0700107 private static final Logger log =
108 LoggerFactory.getLogger(LinkDiscoveryManager.class);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700109 // TODO Remove these factories.
110 protected OFFactory factory13 = OFFactories.getFactory(OFVersion.OF_13);
111 protected OFFactory factory10 = OFFactories.getFactory(OFVersion.OF_10);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800112
Jonathan Hart284e70f2014-07-05 12:32:51 -0700113 private IFloodlightProviderService controller;
114
115 private IFloodlightProviderService floodlightProvider;
116 private IThreadPoolService threadPool;
117 private IRestApiService restApi;
118 private IControllerRegistryService registryService;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700119
Jonathan Hartba354e02014-06-30 19:18:16 -0700120 // LLDP fields
Jonathan Hart0f383542014-07-09 11:38:03 -0700121 static final byte[] LLDP_STANDARD_DST_MAC_STRING =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800122 HexString.fromHexString("01:80:c2:00:00:0e");
Ray Milkey269ffb92014-04-03 14:43:30 -0700123 private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800124 private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
125
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800126 // Link discovery task details.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700127 private SingletonTask discoveryTask;
128 private static final int DISCOVERY_TASK_INTERVAL = 1;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700129 private static final int LINK_TIMEOUT = 35; // original 35 secs, aggressive
130 // 5 secs
131 private static final int LLDP_TO_ALL_INTERVAL = 15; // original 15 seconds,
132 // aggressive 2 secs.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700133 private long lldpClock = 0;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800134 // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
135 // If we want to identify link failures faster, we could decrease this
136 // value to a small number, say 1 or 2 sec.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700137 private static final int LLDP_TO_KNOWN_INTERVAL = 20; // LLDP frequency for
138 // known links
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800139
Jonathan Hart284e70f2014-07-05 12:32:51 -0700140 private ReentrantReadWriteLock lock;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700141
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800142 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700143 * Map from link to the most recent time it was verified functioning.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800144 */
145 protected Map<Link, LinkInfo> links;
146
147 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700148 * Map from switch id to a set of all links with it as an endpoint.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800149 */
150 protected Map<Long, Set<Link>> switchLinks;
151
152 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700153 * Map from a id:port to the set of links containing it as an endpoint.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800154 */
155 protected Map<NodePortTuple, Set<Link>> portLinks;
156
157 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700158 * Listeners are called in the order they were added to the the list.
Ray Milkeyb41100a2014-04-10 10:42:15 -0700159 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700160 private final List<ILinkDiscoveryListener> linkDiscoveryListeners =
161 new CopyOnWriteArrayList<>();
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800162
Jonathan Hart284e70f2014-07-05 12:32:51 -0700163 /**
164 * List of ports through which LLDPs are not sent.
165 */
166 private Set<NodePortTuple> suppressLinkDiscovery;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700167
Jonathan Hart284e70f2014-07-05 12:32:51 -0700168 private enum UpdateType {
169 LINK_ADDED,
170 LINK_REMOVED
171 }
172
173 private class LinkUpdate implements IUpdate {
174 private final Link link;
175 private final UpdateType operation;
176
177 public LinkUpdate(Link link, UpdateType operation) {
178 this.link = link;
179 this.operation = operation;
Ray Milkey269ffb92014-04-03 14:43:30 -0700180 }
181
Ray Milkey269ffb92014-04-03 14:43:30 -0700182 @Override
183 public void dispatch() {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700184 if (log.isTraceEnabled()) {
185 log.trace("Dispatching link discovery update {} for {}",
186 operation, link);
187 }
188 for (ILinkDiscoveryListener listener : linkDiscoveryListeners) {
189 switch (operation) {
190 case LINK_ADDED:
191 listener.linkAdded(link);
192 break;
193 case LINK_REMOVED:
194 listener.linkRemoved(link);
195 break;
196 default:
197 log.warn("Unknown link update operation {}", operation);
198 break;
Jonathan Hartb0904bf2013-11-26 14:41:11 -0800199 }
200 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700201 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700202 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800203
204 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700205 * Gets the LLDP sending period in seconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700206 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800207 * @return LLDP sending period in seconds.
208 */
209 public int getLldpFrequency() {
210 return LLDP_TO_KNOWN_INTERVAL;
211 }
212
213 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700214 * Gets the LLDP timeout value in seconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700215 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800216 * @return LLDP timeout value in seconds
217 */
218 public int getLldpTimeout() {
219 return LINK_TIMEOUT;
220 }
221
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800222 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700223 public Set<NodePortTuple> getDiscoveryDisabledPorts() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800224 return suppressLinkDiscovery;
225 }
226
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800227 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700228 public void disableDiscoveryOnPort(long sw, short port) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800229 NodePortTuple npt = new NodePortTuple(sw, port);
230 this.suppressLinkDiscovery.add(npt);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700231 deleteLinksOnPort(npt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800232 }
233
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800234 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700235 public void enableDiscoveryOnPort(long sw, short port) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800236 NodePortTuple npt = new NodePortTuple(sw, port);
237 this.suppressLinkDiscovery.remove(npt);
238 discover(npt);
239 }
240
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700241 private boolean isLinkDiscoverySuppressed(long sw, short p) {
242 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, p));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800243 }
244
Jonathan Hart284e70f2014-07-05 12:32:51 -0700245 private void discoverLinks() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800246
Jonathan Hart284e70f2014-07-05 12:32:51 -0700247 // time out known links.
248 timeOutLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800249
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700250 // increment LLDP clock
Ray Milkey269ffb92014-04-03 14:43:30 -0700251 lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800252
253 if (lldpClock == 0) {
254 log.debug("Sending LLDP out on all ports.");
255 discoverOnAllPorts();
256 }
257 }
258
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800259 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700260 * Send LLDP on known ports.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800261 */
262 protected void discoverOnKnownLinkPorts() {
263 // Copy the port set.
264 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
265 nptSet.addAll(portLinks.keySet());
266
267 // Send LLDP from each of them.
Ray Milkey269ffb92014-04-03 14:43:30 -0700268 for (NodePortTuple npt : nptSet) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800269 discover(npt);
270 }
271 }
272
Jonathan Hart284e70f2014-07-05 12:32:51 -0700273 private void discover(NodePortTuple npt) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800274 discover(npt.getNodeId(), npt.getPortId());
275 }
276
Jonathan Hart284e70f2014-07-05 12:32:51 -0700277 private void discover(long sw, short port) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700278 sendDiscoveryMessage(sw, port, false);
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800279 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800280
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800281 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700282 * Send link discovery message out of a given switch port. The discovery
283 * message is a standard LLDP containing ONOS-specific TLVs.
Ray Milkey269ffb92014-04-03 14:43:30 -0700284 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700285 * @param sw the switch to send on
286 * @param port the port to send out
287 * @param isReverse indicates whether the LLDP was sent as a response
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800288 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700289 @LogMessageDoc(level = "ERROR",
290 message = "Failure sending LLDP out port {port} on switch {switch}",
291 explanation = "An I/O error occured while sending LLDP message " +
292 "to the switch.",
293 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800294 protected void sendDiscoveryMessage(long sw, short port,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700295 boolean isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800296
297 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
298 if (iofSwitch == null) {
299 return;
300 }
301
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700302 if (port == OFPort.LOCAL.getShortPortNumber()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800303 return;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700304 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800305
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700306 OFPortDesc ofpPort = iofSwitch.getPort(port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800307
308 if (ofpPort == null) {
309 if (log.isTraceEnabled()) {
310 log.trace("Null physical port. sw={}, port={}", sw, port);
311 }
312 return;
313 }
314
315 if (isLinkDiscoverySuppressed(sw, port)) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700316 // Don't send LLDPs out of this port as suppressLLDPs set
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800317 return;
318 }
319
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800320 if (log.isTraceEnabled()) {
321 log.trace("Sending LLDP packet out of swich: {}, port: {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700322 sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800323 }
324
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700325 OFFactory factory = (iofSwitch.getOFVersion() == OFVersion.OF_10)
326 ? factory10 : factory13;
327 OFPacketOut po = createLLDPPacketOut(sw, ofpPort, isReverse, factory);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800328
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800329 try {
330 iofSwitch.write(po, null);
331 iofSwitch.flush();
332 } catch (IOException e) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700333 log.error("Failure sending LLDP out port " + port + " on switch "
334 + iofSwitch.getStringId(), e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800335 }
336
337 }
338
339 /**
Jonathan Hart299d1132014-06-27 09:25:28 -0700340 * Creates packet_out LLDP for specified output port.
341 *
342 * @param dpid the dpid of the outgoing switch
343 * @param port the outgoing port
344 * @param isReverse whether this is a reverse LLDP or not
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700345 * @param factory the factory to use to create the message
Jonathan Hart299d1132014-06-27 09:25:28 -0700346 * @return Packet_out message with LLDP data
347 */
348 private OFPacketOut createLLDPPacketOut(long dpid,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700349 final OFPortDesc port, boolean isReverse, OFFactory factory) {
Jonathan Hart299d1132014-06-27 09:25:28 -0700350 // Set up packets
351 // TODO optimize by not creating new packets each time
352 OnosLldp lldpPacket = new OnosLldp();
353
354 Ethernet ethPacket = new Ethernet();
355 ethPacket.setEtherType(Ethernet.TYPE_LLDP);
356 ethPacket.setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING);
357 ethPacket.setPayload(lldpPacket);
358 ethPacket.setPad(true);
359
Jonathan Hart299d1132014-06-27 09:25:28 -0700360 lldpPacket.setSwitch(dpid);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700361 lldpPacket.setPort(port.getPortNo().getShortPortNumber());
Jonathan Hart299d1132014-06-27 09:25:28 -0700362 lldpPacket.setReverse(isReverse);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700363 ethPacket.setSourceMACAddress(port.getHwAddr().getBytes());
Jonathan Hart299d1132014-06-27 09:25:28 -0700364 final byte[] lldp = ethPacket.serialize();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700365
366 List<OFAction> actions = new ArrayList<OFAction>();
367 actions.add(factory.actions()
368 .buildOutput()
369 .setPort(OFPort.ofShort(port.getPortNo().getShortPortNumber()))
370 .build());
371 OFPacketOut po = factory.buildPacketOut()
372 .setData(lldp)
373 .setBufferId(OFBufferId.NO_BUFFER)
374 .setInPort(OFPort.CONTROLLER)
375 .setActions(actions)
376 .build();
377
378 return po;
Jonathan Hart299d1132014-06-27 09:25:28 -0700379 }
380
381 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700382 * Send LLDPs to all switch-ports.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800383 */
384 protected void discoverOnAllPorts() {
385 if (log.isTraceEnabled()) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800386 log.trace("Sending LLDP packets out of all the enabled ports on switch");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800387 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700388
389 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
390 if (sw.getEnabledPorts() == null) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700391 continue;
392 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700393 for (OFPortDesc ofp : sw.getEnabledPorts()) {
394 if (isLinkDiscoverySuppressed(sw.getId(),
395 ofp.getPortNo().getShortPortNumber())) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700396 continue;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800397 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700398
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700399 sendDiscoveryMessage(sw.getId(),
400 ofp.getPortNo().getShortPortNumber(), false);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800401 }
402 }
403 }
404
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800405 @Override
406 public String getName() {
407 return "linkdiscovery";
408 }
409
410 @Override
411 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
412 switch (msg.getType()) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700413 case PACKET_IN:
414 if (msg instanceof OFPacketIn) {
415 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg,
416 cntx);
417 }
418 break;
419 case PORT_STATUS:
420 if (msg instanceof OFPortStatus) {
421 return this.handlePortStatus(sw, (OFPortStatus) msg);
422 }
423 break;
424 default:
425 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800426 }
427 return Command.CONTINUE;
428 }
429
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700430 protected Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, short inport) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800431 // If LLDP is suppressed on this port, ignore received packet as well
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700432 IOFSwitch iofSwitch = floodlightProvider.getSwitch(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800433 if (iofSwitch == null) {
434 return Command.STOP;
435 }
436
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700437 if (isLinkDiscoverySuppressed(sw, inport)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800438 return Command.STOP;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700439 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800440
441 // If this is a malformed LLDP, or not from us, exit
Ray Milkeyb29e6262014-04-09 16:02:14 -0700442 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800443 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700444 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800445
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800446 // Verify this LLDP packet matches what we're looking for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700447 byte[] packetData = pi.getData();
Jonathan Hart299d1132014-06-27 09:25:28 -0700448 if (!OnosLldp.isOnosLldp(packetData)) {
449 log.trace("Dropping LLDP that wasn't sent by ONOS");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800450 return Command.STOP;
451 }
452
Jonathan Hart299d1132014-06-27 09:25:28 -0700453 SwitchPort switchPort = OnosLldp.extractSwitchPort(packetData);
454 long remoteDpid = switchPort.dpid().value();
Yuta HIGUCHI9da3a6e2014-06-10 22:11:58 -0700455 short remotePort = switchPort.port().shortValue();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700456 IOFSwitch remoteSwitch = floodlightProvider.getSwitches().get(
457 switchPort.dpid().value());
Jonathan Hart299d1132014-06-27 09:25:28 -0700458
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700459 OFPortDesc physicalPort = null;
Jonathan Hart299d1132014-06-27 09:25:28 -0700460 if (remoteSwitch != null) {
461 physicalPort = remoteSwitch.getPort(remotePort);
462 if (!remoteSwitch.portEnabled(remotePort)) {
463 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700464 log.trace("Ignoring link with disabled source 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 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700469 if (suppressLinkDiscovery.contains(
470 new NodePortTuple(remoteSwitch.getId(), remotePort))) {
Jonathan Hart299d1132014-06-27 09:25:28 -0700471 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700472 log.trace("Ignoring link with suppressed src port: " +
473 "switch {} port {}", remoteSwitch, remotePort);
Jonathan Hart299d1132014-06-27 09:25:28 -0700474 }
475 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800476 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800477 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700478 if (!iofSwitch.portEnabled(inport)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800479 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700480 log.trace("Ignoring link with disabled dest port: " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700481 "switch {} port {}", sw, inport);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800482 }
483 return Command.STOP;
484 }
485
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700486 // TODO It probably should be empty Set instead of null. Confirm and fix.
487 Set<OFPortState> srcPortState = (physicalPort != null)
488 ? physicalPort.getState() : null;
489 physicalPort = iofSwitch.getPort(inport);
490 Set<OFPortState> dstPortState = (physicalPort != null)
491 ? physicalPort.getState() : null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800492
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700493 // Store the time of update to this link, and push it out to
494 // routingEngine
495 Link lt = new Link(remoteDpid, remotePort, iofSwitch.getId(), inport);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800496
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700497 LinkInfo linkInfo = new LinkInfo(System.currentTimeMillis(),
498 System.currentTimeMillis(), srcPortState, dstPortState);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800499
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700500 addOrUpdateLink(lt, linkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800501
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800502 // Check if reverse link exists.
503 // If it doesn't exist and if the forward link was seen
504 // first seen within a small interval, send probe on the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800505 // reverse link.
Jonathan Hart299d1132014-06-27 09:25:28 -0700506 boolean isReverse = OnosLldp.isReverse(lldp);
507
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700508 LinkInfo newLinkInfo = links.get(lt);
Jonathan Hartba354e02014-06-30 19:18:16 -0700509 if (newLinkInfo != null && !isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800510 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700511 lt.getSrc(), lt.getSrcPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800512 LinkInfo reverseInfo = links.get(reverseLink);
513 if (reverseInfo == null) {
514 // the reverse link does not exist.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700515 if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis()
516 - LINK_TIMEOUT) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700517 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), true);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800518 }
519 }
520 }
521
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800522 // Consume this message
523 return Command.STOP;
524 }
525
526 protected Command handlePacketIn(long sw, OFPacketIn pi,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700527 FloodlightContext cntx) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800528 Ethernet eth =
529 IFloodlightProviderService.bcStore.get(cntx,
Ray Milkey269ffb92014-04-03 14:43:30 -0700530 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700531 short inport = (short) cntx.getStorage()
532 .get(IFloodlightProviderService.CONTEXT_PI_INPORT);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800533
Jonathan Hartba354e02014-06-30 19:18:16 -0700534 if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700535 return handleLldp((LLDP) eth.getPayload(), sw, pi, inport);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800536 } else if (eth.getEtherType() < 1500) {
537 long destMac = eth.getDestinationMAC().toLong();
Ray Milkey269ffb92014-04-03 14:43:30 -0700538 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800539 if (log.isTraceEnabled()) {
540 log.trace("Ignoring packet addressed to 802.1D/Q " +
541 "reserved address.");
542 }
543 return Command.STOP;
544 }
545 }
546
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800547 return Command.CONTINUE;
548 }
549
Jonathan Hart284e70f2014-07-05 12:32:51 -0700550 protected void addOrUpdateLink(Link lt, LinkInfo detectedLinkInfo) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800551 lock.writeLock().lock();
552 try {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700553 LinkInfo existingInfo = links.get(lt);
554
555 LinkInfo newLinkInfo = new LinkInfo(
556 ((existingInfo == null) ? detectedLinkInfo.getFirstSeenTime()
557 : existingInfo.getFirstSeenTime()),
558 detectedLinkInfo.getLastProbeReceivedTime(),
559 detectedLinkInfo.getSrcPortState(),
560 detectedLinkInfo.getDstPortState());
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700561
Jonathan Hart284e70f2014-07-05 12:32:51 -0700562 // Add new LinkInfo or update old LinkInfo
563 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800564
565 if (log.isTraceEnabled()) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700566 log.trace("addOrUpdateLink: {}", lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800567 }
568
Jonathan Hart284e70f2014-07-05 12:32:51 -0700569 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
570 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800571
Jonathan Hart284e70f2014-07-05 12:32:51 -0700572 // If this is the first time we've seen the link, add the Link
573 // object to the data structures/indexes as well
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700574 if (existingInfo == null) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700575 log.trace("Creating new Link: {}", lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800576 // index it by switch source
Ray Milkeyb29e6262014-04-09 16:02:14 -0700577 if (!switchLinks.containsKey(lt.getSrc())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800578 switchLinks.put(lt.getSrc(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700579 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800580 switchLinks.get(lt.getSrc()).add(lt);
581
582 // index it by switch dest
Ray Milkeyb29e6262014-04-09 16:02:14 -0700583 if (!switchLinks.containsKey(lt.getDst())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800584 switchLinks.put(lt.getDst(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700585 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800586 switchLinks.get(lt.getDst()).add(lt);
587
588 // index both ends by switch:port
Ray Milkeyb29e6262014-04-09 16:02:14 -0700589 if (!portLinks.containsKey(srcNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800590 portLinks.put(srcNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700591 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800592 portLinks.get(srcNpt).add(lt);
593
Ray Milkeyb29e6262014-04-09 16:02:14 -0700594 if (!portLinks.containsKey(dstNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800595 portLinks.put(dstNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700596 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800597 portLinks.get(dstNpt).add(lt);
598
Jonathan Hart284e70f2014-07-05 12:32:51 -0700599 // Publish LINK_ADDED event
600 controller.publishUpdate(new LinkUpdate(lt, UpdateType.LINK_ADDED));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800601 }
602 } finally {
603 lock.writeLock().unlock();
604 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800605 }
606
607 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700608 * Removes links from the data structures.
Ray Milkey269ffb92014-04-03 14:43:30 -0700609 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700610 * @param linksToDelete the list of links to delete
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800611 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700612 protected void deleteLinks(List<Link> linksToDelete) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800613 lock.writeLock().lock();
614 try {
Ray Milkey5df613b2014-04-15 10:50:56 -0700615 for (Link lt : linksToDelete) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700616 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
617 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800618
619 switchLinks.get(lt.getSrc()).remove(lt);
620 switchLinks.get(lt.getDst()).remove(lt);
621 if (switchLinks.containsKey(lt.getSrc()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700622 switchLinks.get(lt.getSrc()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800623 this.switchLinks.remove(lt.getSrc());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700624 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800625 if (this.switchLinks.containsKey(lt.getDst()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700626 this.switchLinks.get(lt.getDst()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800627 this.switchLinks.remove(lt.getDst());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700628 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800629
630 if (this.portLinks.get(srcNpt) != null) {
631 this.portLinks.get(srcNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700632 if (this.portLinks.get(srcNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800633 this.portLinks.remove(srcNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700634 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800635 }
636 if (this.portLinks.get(dstNpt) != null) {
637 this.portLinks.get(dstNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700638 if (this.portLinks.get(dstNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800639 this.portLinks.remove(dstNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700640 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800641 }
642
Jonathan Hart284e70f2014-07-05 12:32:51 -0700643 this.links.remove(lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800644
Jonathan Hart284e70f2014-07-05 12:32:51 -0700645 controller.publishUpdate(new LinkUpdate(lt,
646 UpdateType.LINK_REMOVED));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800647
648 if (log.isTraceEnabled()) {
649 log.trace("Deleted link {}", lt);
650 }
651 }
652 } finally {
653 lock.writeLock().unlock();
654 }
655 }
656
657 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700658 * Handles an OFPortStatus message from a switch. We will add or delete
659 * LinkTupes as well re-compute the topology if needed.
Ray Milkey269ffb92014-04-03 14:43:30 -0700660 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700661 * @param sw The dpid of the switch that sent the port status message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800662 * @param ps The OFPortStatus message
663 * @return The Command to continue or stop after we process this message
664 */
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700665 protected Command handlePortStatus(IOFSwitch sw, OFPortStatus ps) {
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -0700666
Jonathan Hart284e70f2014-07-05 12:32:51 -0700667 // If we do not control this switch, then we should not process its
668 // port status messages
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700669 if (!registryService.hasControl(sw.getId())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700670 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700671 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800672
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800673 if (log.isTraceEnabled()) {
674 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
675 "config is {} state is {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700676 new Object[] {sw.getStringId(),
677 ps.getDesc().getPortNo(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700678 ps.getReason(),
679 ps.getDesc().getConfig(),
680 ps.getDesc().getState()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800681 }
682
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700683 short port = ps.getDesc().getPortNo().getShortPortNumber();
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700684 NodePortTuple npt = new NodePortTuple(sw.getId(), port);
Ray Milkey269ffb92014-04-03 14:43:30 -0700685 boolean linkDeleted = false;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800686 boolean linkInfoChanged = false;
687
688 lock.writeLock().lock();
689 try {
690 // if ps is a delete, or a modify where the port is down or
691 // configured down
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700692 if (OFPortReason.DELETE == ps.getReason() ||
693 (OFPortReason.MODIFY == ps.getReason() &&
694 !portEnabled(ps.getDesc()))) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700695 deleteLinksOnPort(npt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800696 linkDeleted = true;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700697 } else if (ps.getReason() == OFPortReason.MODIFY) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800698 // If ps is a port modification and the port state has changed
699 // that affects links in the topology
700
701 if (this.portLinks.containsKey(npt)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700702 for (Link lt : this.portLinks.get(npt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800703 LinkInfo linkInfo = links.get(lt);
Ray Milkey269ffb92014-04-03 14:43:30 -0700704 assert (linkInfo != null);
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700705 LinkInfo newLinkInfo = null;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800706
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700707 if (lt.isSrcPort(npt) &&
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700708 !linkInfo.getSrcPortState().equals(
709 ps.getDesc().getState())) {
710 // If this port status is for the src port and the
711 // port state has changed, create a new link info
712 // with the new state
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700713
714 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
715 linkInfo.getLastProbeReceivedTime(),
716 ps.getDesc().getState(),
717 linkInfo.getDstPortState());
718 } else if (lt.isDstPort(npt) &&
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700719 !linkInfo.getDstPortState().equals(
720 ps.getDesc().getState())) {
721 // If this port status is for the dst port and the
722 // port state has changed, create a new link info
723 // with the new state
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700724
725 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
726 linkInfo.getLastProbeReceivedTime(),
727 linkInfo.getSrcPortState(),
728 ps.getDesc().getState());
729 }
730
731 if (newLinkInfo != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800732 linkInfoChanged = true;
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700733 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800734 }
735 }
736 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800737 }
738
Ray Milkey269ffb92014-04-03 14:43:30 -0700739 if (!linkDeleted && !linkInfoChanged) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800740 if (log.isTraceEnabled()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700741 log.trace("handlePortStatus: Switch {} port #{} reason {};" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800742 " no links to update/remove",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700743 new Object[] {HexString.toHexString(sw.getId()),
744 ps.getDesc().getPortNo(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700745 ps.getReason()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800746 }
747 }
748 } finally {
749 lock.writeLock().unlock();
750 }
751
752 if (!linkDeleted) {
753 // Send LLDP right away when port state is changed for faster
754 // cluster-merge. If it is a link delete then there is not need
755 // to send the LLDPs right away and instead we wait for the LLDPs
756 // to be sent on the timer as it is normally done
757 // do it outside the write-lock
758 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
759 processNewPort(npt.getNodeId(), npt.getPortId());
760 }
761 return Command.CONTINUE;
762 }
763
764 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700765 * Process a new port. If link discovery is disabled on the port, then do
766 * nothing. Otherwise, send LLDP message.
Ray Milkey269ffb92014-04-03 14:43:30 -0700767 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700768 * @param sw the dpid of the switch the port is on
769 * @param p the number of the port
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800770 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700771 private void processNewPort(long sw, int p) {
772 if (isLinkDiscoverySuppressed(sw, (short) p)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800773 // Do nothing as link discovery is suppressed.
Ray Milkey1aa71f82014-04-08 16:23:24 -0700774 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700775 } else {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700776 discover(sw, (short) p);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800777 }
778 }
779
780 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700781 * We send out LLDP messages when a switch is added to discover the
782 * topology.
Ray Milkey269ffb92014-04-03 14:43:30 -0700783 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700784 * @param swId the datapath Id of the new switch
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800785 */
786 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700787 public void switchActivatedMaster(long swId) {
788 IOFSwitch sw = floodlightProvider.getSwitch(swId);
789 if (sw == null) {
790 log.warn("Added switch not available {} ", swId);
791 return;
792 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800793 if (sw.getEnabledPorts() != null) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700794 for (Integer p : sw.getEnabledPortNumbers()) {
795 processNewPort(swId, p);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800796 }
797 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800798 }
799
800 /**
801 * When a switch disconnects we remove any links from our map and notify.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700802 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700803 * @param swId the datapath Id of the switch that was removed
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800804 */
805 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700806 public void switchDisconnected(long swId) {
807 IOFSwitch sw = floodlightProvider.getSwitch(swId);
808 if (sw == null) {
809 log.warn("Removed switch not available {} ", swId);
810 return;
811 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800812 // Update event history
Jonathan Hart6d208022014-06-24 18:56:36 -0700813
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800814 List<Link> eraseList = new ArrayList<Link>();
815 lock.writeLock().lock();
816 try {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700817 if (switchLinks.containsKey(swId)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800818 if (log.isTraceEnabled()) {
819 log.trace("Handle switchRemoved. Switch {}; removing links {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700820 HexString.toHexString(swId), switchLinks.get(swId));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800821 }
822 // add all tuples with an endpoint on this switch to erase list
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700823 eraseList.addAll(switchLinks.get(swId));
Jonathan Hart284e70f2014-07-05 12:32:51 -0700824 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800825 }
826 } finally {
827 lock.writeLock().unlock();
828 }
829 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800830
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700831 @Override
832 public void switchActivatedEqual(long swId) {
833 // TODO Auto-generated method stub
834
835 }
836
837 @Override
838 public void switchMasterToEqual(long swId) {
839 // TODO Auto-generated method stub
840
841 }
842
843 @Override
844 public void switchEqualToMaster(long swId) {
845 // for now treat as switchActivatedMaster
846 switchActivatedMaster(swId);
847 }
848
Jonathan Hart284e70f2014-07-05 12:32:51 -0700849 /*
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700850 * We don't react to port changed notifications here. we listen for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800851 * OFPortStatus messages directly. Might consider using this notifier
852 * instead
853 */
854 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700855 public void switchPortChanged(long swId, OFPortDesc port,
856 PortChangeType changeType) {
857 // TODO Auto-generated method stub
858
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800859 }
860
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800861 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800862 * Delete links incident on a given switch port.
Ray Milkey269ffb92014-04-03 14:43:30 -0700863 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700864 * @param npt the port to delete links on
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800865 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700866 protected void deleteLinksOnPort(NodePortTuple npt) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800867 List<Link> eraseList = new ArrayList<Link>();
868 if (this.portLinks.containsKey(npt)) {
869 if (log.isTraceEnabled()) {
870 log.trace("handlePortStatus: Switch {} port #{} " +
871 "removing links {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700872 new Object[] {HexString.toHexString(npt.getNodeId()),
Ray Milkey269ffb92014-04-03 14:43:30 -0700873 npt.getPortId(),
874 this.portLinks.get(npt)});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800875 }
876 eraseList.addAll(this.portLinks.get(npt));
Jonathan Hart284e70f2014-07-05 12:32:51 -0700877 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800878 }
879 }
880
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800881 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700882 * Iterates through the list of links and deletes if the last discovery
883 * message reception time exceeds timeout values.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800884 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700885 protected void timeOutLinks() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800886 List<Link> eraseList = new ArrayList<Link>();
887 Long curTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800888
889 // reentrant required here because deleteLink also write locks
890 lock.writeLock().lock();
891 try {
892 Iterator<Entry<Link, LinkInfo>> it =
893 this.links.entrySet().iterator();
894 while (it.hasNext()) {
895 Entry<Link, LinkInfo> entry = it.next();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800896 LinkInfo info = entry.getValue();
897
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700898 if ((info.getLastProbeReceivedTime() + (1000L * LINK_TIMEOUT)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700899 < curTime)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800900 eraseList.add(entry.getKey());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800901 }
902 }
903
Jonathan Hart284e70f2014-07-05 12:32:51 -0700904 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800905 } finally {
906 lock.writeLock().unlock();
907 }
908 }
909
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700910 private boolean portEnabled(OFPortDesc port) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700911 if (port == null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800912 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700913 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700914 if (port.getConfig().contains(OFPortConfig.PORT_DOWN)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800915 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700916 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700917 if (port.getState().contains(OFPortState.LINK_DOWN)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800918 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700919 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800920 return true;
921 }
922
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800923 @Override
924 public Map<Link, LinkInfo> getLinks() {
925 lock.readLock().lock();
926 Map<Link, LinkInfo> result;
927 try {
928 result = new HashMap<Link, LinkInfo>(links);
929 } finally {
930 lock.readLock().unlock();
931 }
932 return result;
933 }
934
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800935 @Override
936 public void addListener(ILinkDiscoveryListener listener) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700937 linkDiscoveryListeners.add(listener);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800938 }
939
Jonathan Hart284e70f2014-07-05 12:32:51 -0700940 @Override
941 public void removeListener(ILinkDiscoveryListener listener) {
942 linkDiscoveryListeners.remove(listener);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800943 }
944
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800945 @Override
946 public boolean isCallbackOrderingPrereq(OFType type, String name) {
947 return false;
948 }
949
950 @Override
951 public boolean isCallbackOrderingPostreq(OFType type, String name) {
952 return false;
953 }
954
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800955 // IFloodlightModule classes
956
957 @Override
958 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800959 Collection<Class<? extends IFloodlightService>> l =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800960 new ArrayList<Class<? extends IFloodlightService>>();
961 l.add(ILinkDiscoveryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800962 return l;
963 }
964
965 @Override
966 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Jonathan Hart284e70f2014-07-05 12:32:51 -0700967 getServiceImpls() {
968 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
969 new HashMap<>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800970 m.put(ILinkDiscoveryService.class, this);
971 return m;
972 }
973
974 @Override
975 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700976 Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800977 l.add(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800978 l.add(IThreadPoolService.class);
979 l.add(IRestApiService.class);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700980 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800981 return l;
982 }
983
984 @Override
985 public void init(FloodlightModuleContext context)
986 throws FloodlightModuleException {
987 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800988 threadPool = context.getServiceImpl(IThreadPoolService.class);
989 restApi = context.getServiceImpl(IRestApiService.class);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700990 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800991
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800992 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800993 this.links = new HashMap<Link, LinkInfo>();
994 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
995 this.suppressLinkDiscovery =
996 Collections.synchronizedSet(new HashSet<NodePortTuple>());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800997 this.switchLinks = new HashMap<Long, Set<Link>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800998 }
999
1000 @Override
1001 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001002 @LogMessageDoc(level = "ERROR",
1003 message = "No storage source found.",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001004 explanation = "Storage source was not initialized; cannot initialize "
1005 +
Ray Milkey269ffb92014-04-03 14:43:30 -07001006 "link discovery.",
1007 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
1008 @LogMessageDoc(level = "ERROR",
1009 message = "Error in installing listener for " +
1010 "switch config table {table}",
1011 explanation = "Failed to install storage notification for the " +
1012 "switch config table",
1013 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
1014 @LogMessageDoc(level = "ERROR",
1015 message = "No storage source found.",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001016 explanation = "Storage source was not initialized; cannot initialize "
1017 +
Ray Milkey269ffb92014-04-03 14:43:30 -07001018 "link discovery.",
1019 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
1020 @LogMessageDoc(level = "ERROR",
1021 message = "Exception in LLDP send timer.",
1022 explanation = "An unknown error occured while sending LLDP " +
1023 "messages to switches.",
1024 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001025 })
1026 public void startUp(FloodlightModuleContext context) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001027 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Jonathan Hart284e70f2014-07-05 12:32:51 -07001028 controller = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001029
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001030 discoveryTask = new SingletonTask(ses, new Runnable() {
1031 @Override
1032 public void run() {
1033 try {
1034 discoverLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001035 } catch (Exception e) {
1036 log.error("Exception in LLDP send timer.", e);
1037 } finally {
Jonathan Hartba354e02014-06-30 19:18:16 -07001038 log.trace("Rescheduling discovery task");
1039 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
1040 TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001041 }
1042 }
1043 });
1044
Jonathan Hartec4f14e2013-12-12 10:46:38 -08001045 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001046
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001047 // Register for the OpenFlow messages we want to receive
1048 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1049 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1050 // Register for switch updates
1051 floodlightProvider.addOFSwitchListener(this);
Jonathan Hartba354e02014-06-30 19:18:16 -07001052 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001053 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001054}