blob: 54592fb5fce0ee98a5aa768b4e0c1049a6c843c2 [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 Hart23701d12014-04-03 10:45:48 -070018package net.onrc.onos.core.linkdiscovery.internal;
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;
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;
40import net.floodlightcontroller.core.IOFSwitchListener;
41import net.floodlightcontroller.core.annotations.LogMessageCategory;
42import net.floodlightcontroller.core.annotations.LogMessageDoc;
43import net.floodlightcontroller.core.annotations.LogMessageDocs;
44import net.floodlightcontroller.core.module.FloodlightModuleContext;
45import net.floodlightcontroller.core.module.FloodlightModuleException;
46import net.floodlightcontroller.core.module.IFloodlightModule;
47import net.floodlightcontroller.core.module.IFloodlightService;
48import net.floodlightcontroller.core.util.SingletonTask;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080049import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080050import net.floodlightcontroller.threadpool.IThreadPoolService;
Jonathan Hart23701d12014-04-03 10:45:48 -070051import net.onrc.onos.core.linkdiscovery.ILinkDiscovery;
Jonathan Harta99ec672014-04-03 11:30:34 -070052import net.onrc.onos.core.linkdiscovery.ILinkDiscovery.LDUpdate;
53import net.onrc.onos.core.linkdiscovery.ILinkDiscovery.UpdateOperation;
Jonathan Hart23701d12014-04-03 10:45:48 -070054import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryListener;
55import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
56import net.onrc.onos.core.linkdiscovery.Link;
57import net.onrc.onos.core.linkdiscovery.LinkInfo;
58import net.onrc.onos.core.linkdiscovery.NodePortTuple;
Jonathan Hart23701d12014-04-03 10:45:48 -070059import net.onrc.onos.core.linkdiscovery.web.LinkDiscoveryWebRoutable;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070060import net.onrc.onos.core.packet.Ethernet;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070061import net.onrc.onos.core.packet.LLDP;
Jonathan Hart299d1132014-06-27 09:25:28 -070062import net.onrc.onos.core.packet.OnosLldp;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070063import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hart299d1132014-06-27 09:25:28 -070064import net.onrc.onos.core.util.SwitchPort;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070065
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080066import org.openflow.protocol.OFMessage;
67import org.openflow.protocol.OFPacketIn;
68import org.openflow.protocol.OFPacketOut;
69import org.openflow.protocol.OFPhysicalPort;
70import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
71import org.openflow.protocol.OFPhysicalPort.OFPortState;
72import org.openflow.protocol.OFPort;
73import org.openflow.protocol.OFPortStatus;
74import org.openflow.protocol.OFPortStatus.OFPortReason;
75import org.openflow.protocol.OFType;
76import org.openflow.protocol.action.OFAction;
77import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart299d1132014-06-27 09:25:28 -070078import org.openflow.protocol.action.OFActionType;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080079import org.openflow.util.HexString;
80import org.slf4j.Logger;
81import org.slf4j.LoggerFactory;
82
83/**
84 * This class sends out LLDP messages containing the sending switch's datapath
Jonathan Hart299d1132014-06-27 09:25:28 -070085 * id as well as the outgoing port number. Received LLDP messages that
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080086 * match a known switch cause a new LinkTuple to be created according to the
87 * invariant rules listed below. This new LinkTuple is also passed to routing
88 * if it exists to trigger updates.
Ray Milkey269ffb92014-04-03 14:43:30 -070089 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080090 * This class also handles removing links that are associated to switch ports
91 * that go down, and switches that are disconnected.
Ray Milkey269ffb92014-04-03 14:43:30 -070092 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080093 * Invariants:
Ray Milkey269ffb92014-04-03 14:43:30 -070094 * -portLinks and switchLinks will not contain empty Sets outside of
Ray Milkeyb41100a2014-04-10 10:42:15 -070095 * critical sections.
Ray Milkey269ffb92014-04-03 14:43:30 -070096 * -portLinks contains LinkTuples where one of the src or dst
Ray Milkeyb41100a2014-04-10 10:42:15 -070097 * SwitchPortTuple matches the map key.
Ray Milkey269ffb92014-04-03 14:43:30 -070098 * -switchLinks contains LinkTuples where one of the src or dst
Ray Milkeyb41100a2014-04-10 10:42:15 -070099 * SwitchPortTuple's id matches the switch id.
Ray Milkey269ffb92014-04-03 14:43:30 -0700100 * -Each LinkTuple will be indexed into switchLinks for both
Ray Milkeyb41100a2014-04-10 10:42:15 -0700101 * src.id and dst.id, and portLinks for each src and dst.
102 * -The updates queue is only added to from within a held write lock.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800103 */
104@LogMessageCategory("Network Topology")
105public class LinkDiscoveryManager
Ray Milkey269ffb92014-04-03 14:43:30 -0700106 implements IOFMessageListener, IOFSwitchListener,
107 ILinkDiscoveryService, IFloodlightModule {
108 protected IFloodlightProviderService controller;
Ray Milkeyec838942014-04-09 11:28:43 -0700109 private static final Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800110
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800111 protected IFloodlightProviderService floodlightProvider;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800112 protected IThreadPoolService threadPool;
113 protected IRestApiService restApi;
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700114 // Registry Service for ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700115 protected IControllerRegistryService registryService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800116
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700117
Jonathan Hartba354e02014-06-30 19:18:16 -0700118 // LLDP fields
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800119 private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800120 HexString.fromHexString("01:80:c2:00:00:0e");
Ray Milkey269ffb92014-04-03 14:43:30 -0700121 private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800122 private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
123
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800124 // Link discovery task details.
125 protected SingletonTask discoveryTask;
Ray Milkey2476cac2014-04-08 11:03:21 -0700126 protected static final int DISCOVERY_TASK_INTERVAL = 1;
127 protected static final int LINK_TIMEOUT = 35; // original 35 secs, aggressive 5 secs
128 protected static final int LLDP_TO_ALL_INTERVAL = 15; //original 15 seconds, aggressive 2 secs.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800129 protected long lldpClock = 0;
130 // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
131 // If we want to identify link failures faster, we could decrease this
132 // value to a small number, say 1 or 2 sec.
Ray Milkey2476cac2014-04-08 11:03:21 -0700133 protected static final int LLDP_TO_KNOWN_INTERVAL = 20; // LLDP frequency for known links
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800134
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800135 protected ReentrantReadWriteLock lock;
136 int lldpTimeCount = 0;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700137
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800138 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700139 * Map from link to the most recent time it was verified functioning.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800140 */
141 protected Map<Link, LinkInfo> links;
142
143 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700144 * Map from switch id to a set of all links with it as an endpoint.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800145 */
146 protected Map<Long, Set<Link>> switchLinks;
147
148 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700149 * Map from a id:port to the set of links containing it as an endpoint.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800150 */
151 protected Map<NodePortTuple, Set<Link>> portLinks;
152
153 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700154 * Topology aware components are called in the order they were added to the
155 * the array.
156 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800157 protected ArrayList<ILinkDiscoveryListener> linkDiscoveryAware;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800158
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700159 protected class LinkUpdate extends LDUpdate {
160
Ray Milkey269ffb92014-04-03 14:43:30 -0700161 public LinkUpdate(LDUpdate old) {
162 super(old);
163 }
164
165 @LogMessageDoc(level = "ERROR",
166 message = "Error in link discovery updates loop",
167 explanation = "An unknown error occured while dispatching " +
168 "link update notifications",
169 recommendation = LogMessageDoc.GENERIC_ACTION)
170 @Override
171 public void dispatch() {
172 if (linkDiscoveryAware != null) {
Jonathan Hartb0904bf2013-11-26 14:41:11 -0800173 if (log.isTraceEnabled()) {
174 log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700175 new Object[]{this.getOperation(),
176 HexString.toHexString(this.getSrc()), this.getSrcPort(),
177 HexString.toHexString(this.getDst()), this.getDstPort(),
178 linkDiscoveryAware});
Jonathan Hartb0904bf2013-11-26 14:41:11 -0800179 }
180 try {
181 for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained
182 lda.linkDiscoveryUpdate(this);
183 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700184 } catch (Exception e) {
Jonathan Hartb0904bf2013-11-26 14:41:11 -0800185 log.error("Error in link discovery updates loop", e);
186 }
187 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700188 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700189 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800190
191 /**
192 * List of ports through which LLDP/BDDPs are not sent.
193 */
194 protected Set<NodePortTuple> suppressLinkDiscovery;
195
Ray Milkey269ffb92014-04-03 14:43:30 -0700196 /**
197 * A list of ports that are quarantined for discovering links through
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800198 * them. Data traffic from these ports are not allowed until the ports
199 * are released from quarantine.
200 */
Jonathan Hartba354e02014-06-30 19:18:16 -0700201 //protected LinkedBlockingQueue<NodePortTuple> quarantineQueue;
202 //protected LinkedBlockingQueue<NodePortTuple> maintenanceQueue;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800203
204 /**
205 * Map of broadcast domain ports and the last time a BDDP was either
206 * sent or received on that port.
207 */
208 protected Map<NodePortTuple, Long> broadcastDomainPortTimeMap;
209
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800210 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800211 * Get the LLDP sending period in seconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700212 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800213 * @return LLDP sending period in seconds.
214 */
215 public int getLldpFrequency() {
216 return LLDP_TO_KNOWN_INTERVAL;
217 }
218
219 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700220 * Get the LLDP timeout value in seconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700221 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800222 * @return LLDP timeout value in seconds
223 */
224 public int getLldpTimeout() {
225 return LINK_TIMEOUT;
226 }
227
228 public Map<NodePortTuple, Set<Link>> getPortLinks() {
229 return portLinks;
230 }
231
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800232 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800233 public Set<NodePortTuple> getSuppressLLDPsInfo() {
234 return suppressLinkDiscovery;
235 }
236
237 /**
238 * Add a switch port to the suppressed LLDP list.
239 * Remove any known links on the switch port.
240 */
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800241 @Override
Pavlin Radoslavov7d21c0a2014-04-10 10:32:59 -0700242 public void addToSuppressLLDPs(long sw, short port) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800243 NodePortTuple npt = new NodePortTuple(sw, port);
244 this.suppressLinkDiscovery.add(npt);
245 deleteLinksOnPort(npt, "LLDP suppressed.");
246 }
247
248 /**
249 * Remove a switch port from the suppressed LLDP list.
250 * Discover links on that switchport.
251 */
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800252 @Override
Pavlin Radoslavov7d21c0a2014-04-10 10:32:59 -0700253 public void removeFromSuppressLLDPs(long sw, short port) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800254 NodePortTuple npt = new NodePortTuple(sw, port);
255 this.suppressLinkDiscovery.remove(npt);
256 discover(npt);
257 }
258
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800259 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800260 public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700261 return ILinkDiscovery.LinkType.DIRECT_LINK;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800262 }
263
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800264
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800265 private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
266 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
267 }
268
269 protected void discoverLinks() {
270
271 // timeout known links.
272 timeoutLinks();
273
274 //increment LLDP clock
Ray Milkey269ffb92014-04-03 14:43:30 -0700275 lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800276
277 if (lldpClock == 0) {
278 log.debug("Sending LLDP out on all ports.");
279 discoverOnAllPorts();
280 }
281 }
282
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800283 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700284 * Send LLDP on known ports.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800285 */
286 protected void discoverOnKnownLinkPorts() {
287 // Copy the port set.
288 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
289 nptSet.addAll(portLinks.keySet());
290
291 // Send LLDP from each of them.
Ray Milkey269ffb92014-04-03 14:43:30 -0700292 for (NodePortTuple npt : nptSet) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800293 discover(npt);
294 }
295 }
296
297 protected void discover(NodePortTuple npt) {
298 discover(npt.getNodeId(), npt.getPortId());
299 }
300
301 protected void discover(long sw, short port) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700302 sendDiscoveryMessage(sw, port, false);
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800303 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800304
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800305 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800306 * Send link discovery message out of a given switch port.
307 * The discovery message may be a standard LLDP or a modified
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800308 * LLDP, where the dst mac address is set to :ff.
Ray Milkey269ffb92014-04-03 14:43:30 -0700309 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800310 * TODO: The modified LLDP will updated in the future and may
311 * use a different eth-type.
Ray Milkey269ffb92014-04-03 14:43:30 -0700312 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800313 * @param sw
314 * @param port
Ray Milkey269ffb92014-04-03 14:43:30 -0700315 * @param isStandard indicates standard or modified LLDP
316 * @param isReverse indicates whether the LLDP was sent as a response
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800317 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700318 @LogMessageDoc(level = "ERROR",
319 message = "Failure sending LLDP out port {port} on switch {switch}",
320 explanation = "An I/O error occured while sending LLDP message " +
321 "to the switch.",
322 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800323 protected void sendDiscoveryMessage(long sw, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700324 boolean isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800325
326 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
327 if (iofSwitch == null) {
328 return;
329 }
330
Ray Milkeyb29e6262014-04-09 16:02:14 -0700331 if (port == OFPort.OFPP_LOCAL.getValue()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800332 return;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700333 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800334
335 OFPhysicalPort ofpPort = iofSwitch.getPort(port);
336
337 if (ofpPort == null) {
338 if (log.isTraceEnabled()) {
339 log.trace("Null physical port. sw={}, port={}", sw, port);
340 }
341 return;
342 }
343
344 if (isLinkDiscoverySuppressed(sw, port)) {
345 /* Dont send LLDPs out of this port as suppressLLDPs set
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800346 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800347 */
348 return;
349 }
350
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800351 if (log.isTraceEnabled()) {
352 log.trace("Sending LLDP packet out of swich: {}, port: {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700353 sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800354 }
355
Jonathan Hart299d1132014-06-27 09:25:28 -0700356 OFPacketOut po = createLLDPPacketOut(sw, ofpPort, isReverse);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800357
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800358 try {
359 iofSwitch.write(po, null);
360 iofSwitch.flush();
361 } catch (IOException e) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700362 log.error("Failure sending LLDP out port " + port + " on switch " + iofSwitch.getStringId(), e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800363 }
364
365 }
366
367 /**
Jonathan Hart299d1132014-06-27 09:25:28 -0700368 * Creates packet_out LLDP for specified output port.
369 *
370 * @param dpid the dpid of the outgoing switch
371 * @param port the outgoing port
372 * @param isReverse whether this is a reverse LLDP or not
373 * @return Packet_out message with LLDP data
374 */
375 private OFPacketOut createLLDPPacketOut(long dpid,
376 final OFPhysicalPort port, boolean isReverse) {
377 // Set up packets
378 // TODO optimize by not creating new packets each time
379 OnosLldp lldpPacket = new OnosLldp();
380
381 Ethernet ethPacket = new Ethernet();
382 ethPacket.setEtherType(Ethernet.TYPE_LLDP);
383 ethPacket.setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING);
384 ethPacket.setPayload(lldpPacket);
385 ethPacket.setPad(true);
386
387 final OFPacketOut packetOut = (OFPacketOut) floodlightProvider.getOFMessageFactory()
388 .getMessage(OFType.PACKET_OUT);
389 packetOut.setBufferId(OFPacketOut.BUFFER_ID_NONE);
390
391 final List<OFAction> actionsList = new LinkedList<OFAction>();
392 final OFActionOutput out = (OFActionOutput) floodlightProvider.getOFMessageFactory()
393 .getAction(OFActionType.OUTPUT);
394 out.setPort(port.getPortNumber());
395 actionsList.add(out);
396 packetOut.setActions(actionsList);
397 final short alen = (short) OFActionOutput.MINIMUM_LENGTH;
398
399 lldpPacket.setSwitch(dpid);
400 lldpPacket.setPort(port.getPortNumber());
401 lldpPacket.setReverse(isReverse);
402 ethPacket.setSourceMACAddress(port.getHardwareAddress());
403
404 final byte[] lldp = ethPacket.serialize();
405 packetOut.setActionsLength(alen);
406 packetOut.setPacketData(lldp);
407 packetOut
408 .setLength((short) (OFPacketOut.MINIMUM_LENGTH + alen + lldp.length));
409 return packetOut;
410 }
411
412 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700413 * Send LLDPs to all switch-ports.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800414 */
415 protected void discoverOnAllPorts() {
416 if (log.isTraceEnabled()) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800417 log.trace("Sending LLDP packets out of all the enabled ports on switch");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800418 }
419 Set<Long> switches = floodlightProvider.getSwitches().keySet();
420 // Send standard LLDPs
Ray Milkey269ffb92014-04-03 14:43:30 -0700421 for (long sw : switches) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800422 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700423 if (iofSwitch == null) {
424 continue;
425 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800426 if (iofSwitch.getEnabledPorts() != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700427 for (OFPhysicalPort ofp : iofSwitch.getEnabledPorts()) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700428 if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800429 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700430 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800431
432 // sends forward LLDP only non-fastports.
Jonathan Hartba354e02014-06-30 19:18:16 -0700433 sendDiscoveryMessage(sw, ofp.getPortNumber(), false);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800434 }
435 }
436 }
437 }
438
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800439 @Override
440 public String getName() {
441 return "linkdiscovery";
442 }
443
444 @Override
445 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
446 switch (msg.getType()) {
447 case PACKET_IN:
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700448 if (msg instanceof OFPacketIn) {
449 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg,
450 cntx);
451 }
452 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800453 case PORT_STATUS:
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700454 if (msg instanceof OFPortStatus) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700455 return this.handlePortStatus(sw, (OFPortStatus) msg);
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700456 }
457 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800458 default:
459 break;
460 }
461 return Command.CONTINUE;
462 }
463
Jonathan Hartba354e02014-06-30 19:18:16 -0700464 private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800465 // If LLDP is suppressed on this port, ignore received packet as well
466 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
467 if (iofSwitch == null) {
468 return Command.STOP;
469 }
470
Ray Milkeyb29e6262014-04-09 16:02:14 -0700471 if (isLinkDiscoverySuppressed(sw, pi.getInPort())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800472 return Command.STOP;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700473 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800474
475 // If this is a malformed LLDP, or not from us, exit
Ray Milkeyb29e6262014-04-09 16:02:14 -0700476 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800477 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700478 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800479
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800480 // Verify this LLDP packet matches what we're looking for
Jonathan Hart299d1132014-06-27 09:25:28 -0700481 byte[] packetData = pi.getPacketData();
482 if (!OnosLldp.isOnosLldp(packetData)) {
483 log.trace("Dropping LLDP that wasn't sent by ONOS");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800484 return Command.STOP;
485 }
486
Jonathan Hart299d1132014-06-27 09:25:28 -0700487 SwitchPort switchPort = OnosLldp.extractSwitchPort(packetData);
488 long remoteDpid = switchPort.dpid().value();
489 short remotePort = switchPort.port().value();
490 IOFSwitch remoteSwitch = floodlightProvider.getSwitches().get(switchPort.dpid().value());
491
492
493 OFPhysicalPort physicalPort = null;
494 if (remoteSwitch != null) {
495 physicalPort = remoteSwitch.getPort(remotePort);
496 if (!remoteSwitch.portEnabled(remotePort)) {
497 if (log.isTraceEnabled()) {
498 log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
499 }
500 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800501 }
Jonathan Hart299d1132014-06-27 09:25:28 -0700502 if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
503 remotePort))) {
504 if (log.isTraceEnabled()) {
505 log.trace("Ignoring link with suppressed src port: switch {} port {}",
506 remoteSwitch, remotePort);
507 }
508 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800509 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800510 }
511 if (!iofSwitch.portEnabled(pi.getInPort())) {
512 if (log.isTraceEnabled()) {
513 log.trace("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort());
514 }
515 return Command.STOP;
516 }
517
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800518 int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
519 physicalPort = iofSwitch.getPort(pi.getInPort());
520 int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
521
522 // Store the time of update to this link, and push it out to routingEngine
Jonathan Hart299d1132014-06-27 09:25:28 -0700523 Link lt = new Link(remoteDpid, remotePort, iofSwitch.getId(), pi.getInPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800524
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700525 LinkInfo linkInfo = new LinkInfo(System.currentTimeMillis(),
526 System.currentTimeMillis(), srcPortState, dstPortState);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800527
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700528 addOrUpdateLink(lt, linkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800529
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800530 // Check if reverse link exists.
531 // If it doesn't exist and if the forward link was seen
532 // first seen within a small interval, send probe on the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800533 // reverse link.
Jonathan Hart299d1132014-06-27 09:25:28 -0700534 boolean isReverse = OnosLldp.isReverse(lldp);
535
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700536 LinkInfo newLinkInfo = links.get(lt);
Jonathan Hartba354e02014-06-30 19:18:16 -0700537 if (newLinkInfo != null && !isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800538 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700539 lt.getSrc(), lt.getSrcPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800540 LinkInfo reverseInfo = links.get(reverseLink);
541 if (reverseInfo == null) {
542 // the reverse link does not exist.
543 if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - LINK_TIMEOUT) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700544 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), true);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800545 }
546 }
547 }
548
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800549 // Consume this message
550 return Command.STOP;
551 }
552
553 protected Command handlePacketIn(long sw, OFPacketIn pi,
554 FloodlightContext cntx) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800555 Ethernet eth =
556 IFloodlightProviderService.bcStore.get(cntx,
Ray Milkey269ffb92014-04-03 14:43:30 -0700557 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800558
Jonathan Hartba354e02014-06-30 19:18:16 -0700559 if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
560 return handleLldp((LLDP) eth.getPayload(), sw, pi);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800561 } else if (eth.getEtherType() < 1500) {
562 long destMac = eth.getDestinationMAC().toLong();
Ray Milkey269ffb92014-04-03 14:43:30 -0700563 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800564 if (log.isTraceEnabled()) {
565 log.trace("Ignoring packet addressed to 802.1D/Q " +
566 "reserved address.");
567 }
568 return Command.STOP;
569 }
570 }
571
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800572 return Command.CONTINUE;
573 }
574
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700575 protected boolean addOrUpdateLink(Link lt, LinkInfo detectedLinkInfo) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800576
577 NodePortTuple srcNpt, dstNpt;
578 boolean linkChanged = false;
579
580 lock.writeLock().lock();
581 try {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700582 LinkInfo existingInfo = links.get(lt);
583
584 LinkInfo newLinkInfo = new LinkInfo(
585 ((existingInfo == null) ? detectedLinkInfo.getFirstSeenTime()
586 : existingInfo.getFirstSeenTime()),
587 detectedLinkInfo.getLastProbeReceivedTime(),
588 detectedLinkInfo.getSrcPortState(),
589 detectedLinkInfo.getDstPortState());
590 links.put(lt, newLinkInfo);
591
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800592
593 if (log.isTraceEnabled()) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700594 log.trace("addOrUpdateLink: {}", lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800595 }
596
597 UpdateOperation updateOperation = null;
598 linkChanged = false;
599
600 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
601 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
602
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700603 if (existingInfo == null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800604 // index it by switch source
Ray Milkeyb29e6262014-04-09 16:02:14 -0700605 if (!switchLinks.containsKey(lt.getSrc())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800606 switchLinks.put(lt.getSrc(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700607 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800608 switchLinks.get(lt.getSrc()).add(lt);
609
610 // index it by switch dest
Ray Milkeyb29e6262014-04-09 16:02:14 -0700611 if (!switchLinks.containsKey(lt.getDst())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800612 switchLinks.put(lt.getDst(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700613 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800614 switchLinks.get(lt.getDst()).add(lt);
615
616 // index both ends by switch:port
Ray Milkeyb29e6262014-04-09 16:02:14 -0700617 if (!portLinks.containsKey(srcNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800618 portLinks.put(srcNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700619 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800620 portLinks.get(srcNpt).add(lt);
621
Ray Milkeyb29e6262014-04-09 16:02:14 -0700622 if (!portLinks.containsKey(dstNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800623 portLinks.put(dstNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700624 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800625 portLinks.get(dstNpt).add(lt);
626
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700627 // ONOS: Distinguish added event separately from updated event
Pankaj Berdea41016d2013-06-10 21:18:18 -0700628 updateOperation = UpdateOperation.LINK_ADDED;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800629 linkChanged = true;
630
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800631 }
632
633 if (linkChanged) {
634 // find out if the link was added or removed here.
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700635 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700636 lt.getDst(), lt.getDstPort(),
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700637 getLinkType(lt, newLinkInfo),
Ray Milkey269ffb92014-04-03 14:43:30 -0700638 updateOperation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700639 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800640 }
641 } finally {
642 lock.writeLock().unlock();
643 }
644
645 return linkChanged;
646 }
647
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800648 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800649 public Map<Long, Set<Link>> getSwitchLinks() {
650 return this.switchLinks;
651 }
652
653 /**
654 * Removes links from memory and storage.
Ray Milkey269ffb92014-04-03 14:43:30 -0700655 *
Ray Milkey5df613b2014-04-15 10:50:56 -0700656 * @param linksToDelete The List of @LinkTuple to delete.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800657 */
Ray Milkey5df613b2014-04-15 10:50:56 -0700658 protected void deleteLinks(List<Link> linksToDelete, String reason) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800659 lock.writeLock().lock();
660 try {
Ray Milkey5df613b2014-04-15 10:50:56 -0700661 for (Link lt : linksToDelete) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700662 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
663 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800664
665 switchLinks.get(lt.getSrc()).remove(lt);
666 switchLinks.get(lt.getDst()).remove(lt);
667 if (switchLinks.containsKey(lt.getSrc()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700668 switchLinks.get(lt.getSrc()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800669 this.switchLinks.remove(lt.getSrc());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700670 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800671 if (this.switchLinks.containsKey(lt.getDst()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700672 this.switchLinks.get(lt.getDst()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800673 this.switchLinks.remove(lt.getDst());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700674 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800675
676 if (this.portLinks.get(srcNpt) != null) {
677 this.portLinks.get(srcNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700678 if (this.portLinks.get(srcNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800679 this.portLinks.remove(srcNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700680 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800681 }
682 if (this.portLinks.get(dstNpt) != null) {
683 this.portLinks.get(dstNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700684 if (this.portLinks.get(dstNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800685 this.portLinks.remove(dstNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700686 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800687 }
688
689 LinkInfo info = this.links.remove(lt);
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700690 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700691 lt.getDst(), lt.getDstPort(),
692 getLinkType(lt, info),
693 UpdateOperation.LINK_REMOVED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700694 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800695
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800696 // TODO Whenever link is removed, it has to checked if
697 // the switchports must be added to quarantine.
698
699 if (log.isTraceEnabled()) {
700 log.trace("Deleted link {}", lt);
701 }
702 }
703 } finally {
704 lock.writeLock().unlock();
705 }
706 }
707
708 /**
709 * Handles an OFPortStatus message from a switch. We will add or
710 * delete LinkTupes as well re-compute the topology if needed.
Ray Milkey269ffb92014-04-03 14:43:30 -0700711 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800712 * @param sw The IOFSwitch that sent the port status message
713 * @param ps The OFPortStatus message
714 * @return The Command to continue or stop after we process this message
715 */
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700716 protected Command handlePortStatus(IOFSwitch sw, OFPortStatus ps) {
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -0700717
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700718 // ONOS: If we do not control this switch, then we should not process its port status messages
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700719 if (!registryService.hasControl(sw.getId())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700720 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700721 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800722
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800723 if (log.isTraceEnabled()) {
724 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
725 "config is {} state is {}",
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700726 new Object[]{sw.getStringId(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700727 ps.getDesc().getPortNumber(),
728 ps.getReason(),
729 ps.getDesc().getConfig(),
730 ps.getDesc().getState()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800731 }
732
733 short port = ps.getDesc().getPortNumber();
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700734 NodePortTuple npt = new NodePortTuple(sw.getId(), port);
Ray Milkey269ffb92014-04-03 14:43:30 -0700735 boolean linkDeleted = false;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800736 boolean linkInfoChanged = false;
737
738 lock.writeLock().lock();
739 try {
740 // if ps is a delete, or a modify where the port is down or
741 // configured down
Ray Milkey269ffb92014-04-03 14:43:30 -0700742 if ((byte) OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
743 ((byte) OFPortReason.OFPPR_MODIFY.ordinal() ==
744 ps.getReason() && !portEnabled(ps.getDesc()))) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700745
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800746 deleteLinksOnPort(npt, "Port Status Changed");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800747 linkDeleted = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700748 } else if (ps.getReason() ==
749 (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800750 // If ps is a port modification and the port state has changed
751 // that affects links in the topology
752
753 if (this.portLinks.containsKey(npt)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700754 for (Link lt : this.portLinks.get(npt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800755 LinkInfo linkInfo = links.get(lt);
Ray Milkey269ffb92014-04-03 14:43:30 -0700756 assert (linkInfo != null);
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700757 LinkInfo newLinkInfo = null;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800758
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700759 if (lt.isSrcPort(npt) &&
760 linkInfo.getSrcPortState() != ps.getDesc().getState()) {
761 // If this port status is for the src port and the port
762 // state has changed, create a new link info with the new state
763
764 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
765 linkInfo.getLastProbeReceivedTime(),
766 ps.getDesc().getState(),
767 linkInfo.getDstPortState());
768 } else if (lt.isDstPort(npt) &&
769 linkInfo.getDstPortState() != ps.getDesc().getState()) {
770 // If this port status is for the dst port and the port
771 // state has changed, create a new link info with the new state
772
773 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
774 linkInfo.getLastProbeReceivedTime(),
775 linkInfo.getSrcPortState(),
776 ps.getDesc().getState());
777 }
778
779 if (newLinkInfo != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800780 linkInfoChanged = true;
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700781 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800782 }
783 }
784 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800785 }
786
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700787
Ray Milkey269ffb92014-04-03 14:43:30 -0700788 if (!linkDeleted && !linkInfoChanged) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800789 if (log.isTraceEnabled()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700790 log.trace("handlePortStatus: Switch {} port #{} reason {};" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800791 " no links to update/remove",
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700792 new Object[]{HexString.toHexString(sw.getId()),
Ray Milkey269ffb92014-04-03 14:43:30 -0700793 ps.getDesc().getPortNumber(),
794 ps.getReason()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800795 }
796 }
797 } finally {
798 lock.writeLock().unlock();
799 }
800
801 if (!linkDeleted) {
802 // Send LLDP right away when port state is changed for faster
803 // cluster-merge. If it is a link delete then there is not need
804 // to send the LLDPs right away and instead we wait for the LLDPs
805 // to be sent on the timer as it is normally done
806 // do it outside the write-lock
807 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
808 processNewPort(npt.getNodeId(), npt.getPortId());
809 }
810 return Command.CONTINUE;
811 }
812
813 /**
814 * Process a new port.
815 * If link discovery is disabled on the port, then do nothing.
816 * If autoportfast feature is enabled and the port is a fast port, then
817 * do nothing.
818 * Otherwise, send LLDP message. Add the port to quarantine.
Ray Milkey269ffb92014-04-03 14:43:30 -0700819 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800820 * @param sw
821 * @param p
822 */
823 private void processNewPort(long sw, short p) {
824 if (isLinkDiscoverySuppressed(sw, p)) {
825 // Do nothing as link discovery is suppressed.
Ray Milkey1aa71f82014-04-08 16:23:24 -0700826 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700827 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800828 discover(sw, p);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800829 }
830 }
831
832 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700833 * We send out LLDP messages when a switch is added to discover the topology.
Ray Milkey269ffb92014-04-03 14:43:30 -0700834 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800835 * @param sw The IOFSwitch that connected to the controller
836 */
837 @Override
838 public void addedSwitch(IOFSwitch sw) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800839 if (sw.getEnabledPorts() != null) {
840 for (Short p : sw.getEnabledPortNumbers()) {
841 processNewPort(sw.getId(), p);
842 }
843 }
Jonathan Hart6d208022014-06-24 18:56:36 -0700844
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700845 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), null,
Ray Milkey269ffb92014-04-03 14:43:30 -0700846 UpdateOperation.SWITCH_UPDATED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700847 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800848 }
849
850 /**
851 * When a switch disconnects we remove any links from our map and notify.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800852 */
853 @Override
854 public void removedSwitch(IOFSwitch iofSwitch) {
855 // Update event history
856 long sw = iofSwitch.getId();
Jonathan Hart6d208022014-06-24 18:56:36 -0700857
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800858 List<Link> eraseList = new ArrayList<Link>();
859 lock.writeLock().lock();
860 try {
861 if (switchLinks.containsKey(sw)) {
862 if (log.isTraceEnabled()) {
863 log.trace("Handle switchRemoved. Switch {}; removing links {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700864 HexString.toHexString(sw), switchLinks.get(sw));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800865 }
866 // add all tuples with an endpoint on this switch to erase list
867 eraseList.addAll(switchLinks.get(sw));
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -0700868 deleteLinks(eraseList, "Switch Removed");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800869
870 // Send a switch removed update
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700871 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED));
872 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800873 }
874 } finally {
875 lock.writeLock().unlock();
876 }
877 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800878
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800879 /**
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800880 * We don't react the port changed notifications here. we listen for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800881 * OFPortStatus messages directly. Might consider using this notifier
882 * instead
883 */
884 @Override
885 public void switchPortChanged(Long switchId) {
886 // no-op
887 }
888
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800889 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800890 * Delete links incident on a given switch port.
Ray Milkey269ffb92014-04-03 14:43:30 -0700891 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800892 * @param npt
893 * @param reason
894 */
895 protected void deleteLinksOnPort(NodePortTuple npt, String reason) {
896 List<Link> eraseList = new ArrayList<Link>();
897 if (this.portLinks.containsKey(npt)) {
898 if (log.isTraceEnabled()) {
899 log.trace("handlePortStatus: Switch {} port #{} " +
900 "removing links {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700901 new Object[]{HexString.toHexString(npt.getNodeId()),
902 npt.getPortId(),
903 this.portLinks.get(npt)});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800904 }
905 eraseList.addAll(this.portLinks.get(npt));
906 deleteLinks(eraseList, reason);
907 }
908 }
909
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800910 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800911 * Iterates through the list of links and deletes if the
912 * last discovery message reception time exceeds timeout values.
913 */
914 protected void timeoutLinks() {
915 List<Link> eraseList = new ArrayList<Link>();
916 Long curTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800917
918 // reentrant required here because deleteLink also write locks
919 lock.writeLock().lock();
920 try {
921 Iterator<Entry<Link, LinkInfo>> it =
922 this.links.entrySet().iterator();
923 while (it.hasNext()) {
924 Entry<Link, LinkInfo> entry = it.next();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800925 LinkInfo info = entry.getValue();
926
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700927 if ((info.getLastProbeReceivedTime() + (1000L * LINK_TIMEOUT)
928 < curTime)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800929 eraseList.add(entry.getKey());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800930 }
931 }
932
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700933 deleteLinks(eraseList, "LLDP timeout");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800934 } finally {
935 lock.writeLock().unlock();
936 }
937 }
938
939 private boolean portEnabled(OFPhysicalPort port) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700940 if (port == null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800941 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700942 }
943 if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800944 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700945 }
946 if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800947 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700948 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800949 return true;
950 }
951
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800952 @Override
953 public Map<Link, LinkInfo> getLinks() {
954 lock.readLock().lock();
955 Map<Link, LinkInfo> result;
956 try {
957 result = new HashMap<Link, LinkInfo>(links);
958 } finally {
959 lock.readLock().unlock();
960 }
961 return result;
962 }
963
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800964 @Override
965 public void addListener(ILinkDiscoveryListener listener) {
966 linkDiscoveryAware.add(listener);
967 }
968
969 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700970 * Register a link discovery aware component.
Ray Milkey269ffb92014-04-03 14:43:30 -0700971 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800972 * @param linkDiscoveryAwareComponent
973 */
974 public void addLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
975 // TODO make this a copy on write set or lock it somehow
976 this.linkDiscoveryAware.add(linkDiscoveryAwareComponent);
977 }
978
979 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700980 * Deregister a link discovery aware component.
Ray Milkey269ffb92014-04-03 14:43:30 -0700981 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800982 * @param linkDiscoveryAwareComponent
983 */
984 public void removeLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
985 // TODO make this a copy on write set or lock it somehow
986 this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent);
987 }
988
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800989 @Override
990 public boolean isCallbackOrderingPrereq(OFType type, String name) {
991 return false;
992 }
993
994 @Override
995 public boolean isCallbackOrderingPostreq(OFType type, String name) {
996 return false;
997 }
998
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800999 // IFloodlightModule classes
1000
1001 @Override
1002 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001003 Collection<Class<? extends IFloodlightService>> l =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001004 new ArrayList<Class<? extends IFloodlightService>>();
1005 l.add(ILinkDiscoveryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001006 return l;
1007 }
1008
1009 @Override
1010 public Map<Class<? extends IFloodlightService>, IFloodlightService>
1011 getServiceImpls() {
1012 Map<Class<? extends IFloodlightService>,
Ray Milkey269ffb92014-04-03 14:43:30 -07001013 IFloodlightService> m =
1014 new HashMap<Class<? extends IFloodlightService>,
1015 IFloodlightService>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001016 // We are the class that implements the service
1017 m.put(ILinkDiscoveryService.class, this);
1018 return m;
1019 }
1020
1021 @Override
1022 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001023 Collection<Class<? extends IFloodlightService>> l =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001024 new ArrayList<Class<? extends IFloodlightService>>();
1025 l.add(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001026 l.add(IThreadPoolService.class);
1027 l.add(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001028 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001029 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001030 return l;
1031 }
1032
1033 @Override
1034 public void init(FloodlightModuleContext context)
1035 throws FloodlightModuleException {
1036 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001037 threadPool = context.getServiceImpl(IThreadPoolService.class);
1038 restApi = context.getServiceImpl(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001039 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001040 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001041
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001042 // We create this here because there is no ordering guarantee
1043 this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
1044 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001045 this.links = new HashMap<Link, LinkInfo>();
1046 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
1047 this.suppressLinkDiscovery =
1048 Collections.synchronizedSet(new HashSet<NodePortTuple>());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001049 this.switchLinks = new HashMap<Long, Set<Link>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001050 }
1051
1052 @Override
1053 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001054 @LogMessageDoc(level = "ERROR",
1055 message = "No storage source found.",
1056 explanation = "Storage source was not initialized; cannot initialize " +
1057 "link discovery.",
1058 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
1059 @LogMessageDoc(level = "ERROR",
1060 message = "Error in installing listener for " +
1061 "switch config table {table}",
1062 explanation = "Failed to install storage notification for the " +
1063 "switch config table",
1064 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
1065 @LogMessageDoc(level = "ERROR",
1066 message = "No storage source found.",
1067 explanation = "Storage source was not initialized; cannot initialize " +
1068 "link discovery.",
1069 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
1070 @LogMessageDoc(level = "ERROR",
1071 message = "Exception in LLDP send timer.",
1072 explanation = "An unknown error occured while sending LLDP " +
1073 "messages to switches.",
1074 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001075 })
1076 public void startUp(FloodlightModuleContext context) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001077 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001078 controller =
1079 context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001080
1081 // To be started by the first switch connection
1082 discoveryTask = new SingletonTask(ses, new Runnable() {
1083 @Override
1084 public void run() {
1085 try {
1086 discoverLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001087 } catch (Exception e) {
1088 log.error("Exception in LLDP send timer.", e);
1089 } finally {
Jonathan Hartba354e02014-06-30 19:18:16 -07001090 log.trace("Rescheduling discovery task");
1091 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
1092 TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001093 }
1094 }
1095 });
1096
Jonathan Hartec4f14e2013-12-12 10:46:38 -08001097 // Always reschedule link discovery as we are never in SLAVE role now
1098 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001099
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001100 // Register for the OpenFlow messages we want to receive
1101 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1102 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1103 // Register for switch updates
1104 floodlightProvider.addOFSwitchListener(this);
Jonathan Hartba354e02014-06-30 19:18:16 -07001105 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001106 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001107}