blob: 1dbfdcb6628b79289f7c8752a785799b4220d603 [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
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070018package net.onrc.onos.ofcontroller.linkdiscovery.internal;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080019
20import java.io.IOException;
21import java.net.InetAddress;
22import java.net.InetSocketAddress;
23import java.net.NetworkInterface;
24import java.net.SocketAddress;
25import java.nio.ByteBuffer;
26import java.util.ArrayList;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.HashMap;
30import java.util.HashSet;
31import java.util.Iterator;
32import java.util.List;
33import java.util.Map;
34import java.util.Map.Entry;
35import java.util.Set;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080036import java.util.concurrent.LinkedBlockingQueue;
37import java.util.concurrent.ScheduledExecutorService;
38import java.util.concurrent.TimeUnit;
39import java.util.concurrent.locks.ReentrantReadWriteLock;
40
41import net.floodlightcontroller.core.FloodlightContext;
42import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080043import net.floodlightcontroller.core.IOFMessageListener;
44import net.floodlightcontroller.core.IOFSwitch;
45import net.floodlightcontroller.core.IOFSwitchListener;
46import net.floodlightcontroller.core.annotations.LogMessageCategory;
47import net.floodlightcontroller.core.annotations.LogMessageDoc;
48import net.floodlightcontroller.core.annotations.LogMessageDocs;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070049import net.floodlightcontroller.core.internal.OFSwitchImpl;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080050import net.floodlightcontroller.core.module.FloodlightModuleContext;
51import net.floodlightcontroller.core.module.FloodlightModuleException;
52import net.floodlightcontroller.core.module.IFloodlightModule;
53import net.floodlightcontroller.core.module.IFloodlightService;
54import net.floodlightcontroller.core.util.SingletonTask;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080055import net.floodlightcontroller.packet.BSN;
56import net.floodlightcontroller.packet.Ethernet;
57import net.floodlightcontroller.packet.IPv4;
58import net.floodlightcontroller.packet.LLDP;
59import net.floodlightcontroller.packet.LLDPTLV;
60import net.floodlightcontroller.restserver.IRestApiService;
61import net.floodlightcontroller.routing.Link;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080062import net.floodlightcontroller.threadpool.IThreadPoolService;
63import net.floodlightcontroller.topology.NodePortTuple;
64import net.floodlightcontroller.util.EventHistory;
65import net.floodlightcontroller.util.EventHistory.EvAction;
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -070066import net.onrc.onos.ofcontroller.core.IOnosRemoteSwitch;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070067import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery;
Jonathan Hart2fa28062013-11-25 20:16:28 -080068import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
69import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.UpdateOperation;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070070import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryListener;
71import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
72import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070073import net.onrc.onos.ofcontroller.linkdiscovery.web.LinkDiscoveryWebRoutable;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070074import net.onrc.onos.registry.controller.IControllerRegistryService;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070075
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080076import org.openflow.protocol.OFMessage;
77import org.openflow.protocol.OFPacketIn;
78import org.openflow.protocol.OFPacketOut;
79import org.openflow.protocol.OFPhysicalPort;
80import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
81import org.openflow.protocol.OFPhysicalPort.OFPortState;
82import org.openflow.protocol.OFPort;
83import org.openflow.protocol.OFPortStatus;
84import org.openflow.protocol.OFPortStatus.OFPortReason;
85import org.openflow.protocol.OFType;
86import org.openflow.protocol.action.OFAction;
87import org.openflow.protocol.action.OFActionOutput;
88import org.openflow.util.HexString;
89import org.slf4j.Logger;
90import org.slf4j.LoggerFactory;
91
92/**
93 * This class sends out LLDP messages containing the sending switch's datapath
94 * id as well as the outgoing port number. Received LLrescDP messages that
95 * match a known switch cause a new LinkTuple to be created according to the
96 * invariant rules listed below. This new LinkTuple is also passed to routing
97 * if it exists to trigger updates.
98 *
99 * This class also handles removing links that are associated to switch ports
100 * that go down, and switches that are disconnected.
101 *
102 * Invariants:
103 * -portLinks and switchLinks will not contain empty Sets outside of
104 * critical sections
105 * -portLinks contains LinkTuples where one of the src or dst
106 * SwitchPortTuple matches the map key
107 * -switchLinks contains LinkTuples where one of the src or dst
108 * SwitchPortTuple's id matches the switch id
109 * -Each LinkTuple will be indexed into switchLinks for both
110 * src.id and dst.id, and portLinks for each src and dst
111 * -The updates queue is only added to from within a held write lock
112 */
113@LogMessageCategory("Network Topology")
114public class LinkDiscoveryManager
115implements IOFMessageListener, IOFSwitchListener,
Jonathan Hartec4f14e2013-12-12 10:46:38 -0800116ILinkDiscoveryService, IFloodlightModule {
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700117 protected IFloodlightProviderService controller;
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700118 protected final static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800119
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800120 protected IFloodlightProviderService floodlightProvider;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800121 protected IThreadPoolService threadPool;
122 protected IRestApiService restApi;
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700123 // Registry Service for ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700124 protected IControllerRegistryService registryService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800125
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700126
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800127 // LLDP and BDDP fields
128 private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
129 HexString.fromHexString("01:80:c2:00:00:0e");
130 private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
131 private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
132
133 // BigSwitch OUI is 5C:16:C7, so 5D:16:C7 is the multicast version
Masayoshi Kobayashi71137362013-07-12 15:54:52 -0700134 // private static final String LLDP_BSN_DST_MAC_STRING = "5d:16:c7:00:00:01";
135 private static final String LLDP_BSN_DST_MAC_STRING = "ff:ff:ff:ff:ff:ff";
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800136
137
138 // Direction TLVs are used to indicate if the LLDPs were sent
139 // periodically or in response to a recieved LLDP
140 private static final byte TLV_DIRECTION_TYPE = 0x73;
141 private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte
142 private static final byte TLV_DIRECTION_VALUE_FORWARD[] = {0x01};
143 private static final byte TLV_DIRECTION_VALUE_REVERSE[] = {0x02};
144 private static final LLDPTLV forwardTLV
145 = new LLDPTLV().
146 setType((byte)TLV_DIRECTION_TYPE).
147 setLength((short)TLV_DIRECTION_LENGTH).
148 setValue(TLV_DIRECTION_VALUE_FORWARD);
149
150 private static final LLDPTLV reverseTLV
151 = new LLDPTLV().
152 setType((byte)TLV_DIRECTION_TYPE).
153 setLength((short)TLV_DIRECTION_LENGTH).
154 setValue(TLV_DIRECTION_VALUE_REVERSE);
155
156 // Link discovery task details.
157 protected SingletonTask discoveryTask;
158 protected final int DISCOVERY_TASK_INTERVAL = 1;
Umesh Krishnaswamy77601202013-04-03 21:46:01 -0700159 protected final int LINK_TIMEOUT = 35; // original 35 secs, aggressive 5 secs
160 protected final int LLDP_TO_ALL_INTERVAL = 15 ; //original 15 seconds, aggressive 2 secs.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800161 protected long lldpClock = 0;
162 // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
163 // If we want to identify link failures faster, we could decrease this
164 // value to a small number, say 1 or 2 sec.
Umesh Krishnaswamy77601202013-04-03 21:46:01 -0700165 protected final int LLDP_TO_KNOWN_INTERVAL= 20; // LLDP frequency for known links
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800166
167 protected LLDPTLV controllerTLV;
168 protected ReentrantReadWriteLock lock;
169 int lldpTimeCount = 0;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700170
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800171 /**
172 * Flag to indicate if automatic port fast is enabled or not.
173 * Default is set to false -- Initialized in the init method as well.
174 */
175 boolean autoPortFastFeature = false;
176
177 /**
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800178 * Map of remote switches that are not connected to this controller. This
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700179 * is used to learn remote switches in a distributed controller ONOS.
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800180 */
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -0700181 protected Map<Long, IOnosRemoteSwitch> remoteSwitches;
HIGUCHI Yuta3d96f652013-06-17 12:07:48 -0700182
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800183 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800184 * Map from link to the most recent time it was verified functioning
185 */
186 protected Map<Link, LinkInfo> links;
187
188 /**
189 * Map from switch id to a set of all links with it as an endpoint
190 */
191 protected Map<Long, Set<Link>> switchLinks;
192
193 /**
194 * Map from a id:port to the set of links containing it as an endpoint
195 */
196 protected Map<NodePortTuple, Set<Link>> portLinks;
197
198 /**
199 * Set of link tuples over which multicast LLDPs are received
200 * and unicast LLDPs are not received.
201 */
202 protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks;
203
204 protected volatile boolean shuttingDown = false;
205
206 /* topology aware components are called in the order they were added to the
207 * the array */
208 protected ArrayList<ILinkDiscoveryListener> linkDiscoveryAware;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700209
210 protected class LinkUpdate extends LDUpdate {
211
212 public LinkUpdate(LDUpdate old) {
213 super(old);
214 }
215 @LogMessageDoc(level="ERROR",
216 message="Error in link discovery updates loop",
217 explanation="An unknown error occured while dispatching " +
218 "link update notifications",
219 recommendation=LogMessageDoc.GENERIC_ACTION)
220 @Override
221 public void dispatch() {
Jonathan Hartb0904bf2013-11-26 14:41:11 -0800222 if (linkDiscoveryAware != null) {
223 if (log.isTraceEnabled()) {
224 log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
225 new Object[]{this.getOperation(),
226 HexString.toHexString(this.getSrc()), this.getSrcPort(),
227 HexString.toHexString(this.getDst()), this.getDstPort(),
228 linkDiscoveryAware});
229 }
230 try {
231 for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained
232 lda.linkDiscoveryUpdate(this);
233 }
234 }
235 catch (Exception e) {
236 log.error("Error in link discovery updates loop", e);
237 }
238 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700239 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700240 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800241
242 /**
243 * List of ports through which LLDP/BDDPs are not sent.
244 */
245 protected Set<NodePortTuple> suppressLinkDiscovery;
246
247 /** A list of ports that are quarantined for discovering links through
248 * them. Data traffic from these ports are not allowed until the ports
249 * are released from quarantine.
250 */
251 protected LinkedBlockingQueue<NodePortTuple> quarantineQueue;
252 protected LinkedBlockingQueue<NodePortTuple> maintenanceQueue;
253 /**
254 * Quarantine task
255 */
256 protected SingletonTask bddpTask;
257 protected final int BDDP_TASK_INTERVAL = 100; // 100 ms.
258 protected final int BDDP_TASK_SIZE = 5; // # of ports per iteration
259
260 /**
261 * Map of broadcast domain ports and the last time a BDDP was either
262 * sent or received on that port.
263 */
264 protected Map<NodePortTuple, Long> broadcastDomainPortTimeMap;
265
266 /**
267 * Get the LLDP sending period in seconds.
268 * @return LLDP sending period in seconds.
269 */
270 public int getLldpFrequency() {
271 return LLDP_TO_KNOWN_INTERVAL;
272 }
273
274 /**
275 * Get the LLDP timeout value in seconds
276 * @return LLDP timeout value in seconds
277 */
278 public int getLldpTimeout() {
279 return LINK_TIMEOUT;
280 }
281
282 public Map<NodePortTuple, Set<Link>> getPortLinks() {
283 return portLinks;
284 }
285
286 public Set<NodePortTuple> getSuppressLLDPsInfo() {
287 return suppressLinkDiscovery;
288 }
289
290 /**
291 * Add a switch port to the suppressed LLDP list.
292 * Remove any known links on the switch port.
293 */
294 public void AddToSuppressLLDPs(long sw, short port)
295 {
296 NodePortTuple npt = new NodePortTuple(sw, port);
297 this.suppressLinkDiscovery.add(npt);
298 deleteLinksOnPort(npt, "LLDP suppressed.");
299 }
300
301 /**
302 * Remove a switch port from the suppressed LLDP list.
303 * Discover links on that switchport.
304 */
305 public void RemoveFromSuppressLLDPs(long sw, short port)
306 {
307 NodePortTuple npt = new NodePortTuple(sw, port);
308 this.suppressLinkDiscovery.remove(npt);
309 discover(npt);
310 }
311
312 public boolean isShuttingDown() {
313 return shuttingDown;
314 }
315
316 public boolean isFastPort(long sw, short port) {
317 return false;
318 }
319
320 public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
321 if (info.getUnicastValidTime() != null) {
322 return ILinkDiscovery.LinkType.DIRECT_LINK;
323 } else if (info.getMulticastValidTime() != null) {
324 return ILinkDiscovery.LinkType.MULTIHOP_LINK;
325 }
326 return ILinkDiscovery.LinkType.INVALID_LINK;
327 }
328
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700329
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800330 private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
331 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
332 }
333
334 protected void discoverLinks() {
335
336 // timeout known links.
337 timeoutLinks();
338
339 //increment LLDP clock
340 lldpClock = (lldpClock + 1)% LLDP_TO_ALL_INTERVAL;
341
342 if (lldpClock == 0) {
343 log.debug("Sending LLDP out on all ports.");
344 discoverOnAllPorts();
345 }
346 }
347
348
349 /**
350 * Quarantine Ports.
351 */
352 protected class QuarantineWorker implements Runnable {
353 @Override
354 public void run() {
355 try {
356 processBDDPLists();
357 }
358 catch (Exception e) {
359 log.error("Error in quarantine worker thread", e);
360 } finally {
361 bddpTask.reschedule(BDDP_TASK_INTERVAL,
362 TimeUnit.MILLISECONDS);
363 }
364 }
365 }
366
367 /**
368 * Add a switch port to the quarantine queue. Schedule the
369 * quarantine task if the quarantine queue was empty before adding
370 * this switch port.
371 * @param npt
372 */
373 protected void addToQuarantineQueue(NodePortTuple npt) {
374 if (quarantineQueue.contains(npt) == false)
375 quarantineQueue.add(npt);
376 }
377
378 /**
379 * Remove a switch port from the quarantine queue.
380 */
381 protected void removeFromQuarantineQueue(NodePortTuple npt) {
382 // Remove all occurrences of the node port tuple from the list.
383 while (quarantineQueue.remove(npt));
384 }
385
386 /**
387 * Add a switch port to maintenance queue.
388 * @param npt
389 */
390 protected void addToMaintenanceQueue(NodePortTuple npt) {
391 // TODO We are not checking if the switch port tuple is already
392 // in the maintenance list or not. This will be an issue for
393 // really large number of switch ports in the network.
394 if (maintenanceQueue.contains(npt) == false)
395 maintenanceQueue.add(npt);
396 }
397
398 /**
399 * Remove a switch port from maintenance queue.
400 * @param npt
401 */
402 protected void removeFromMaintenanceQueue(NodePortTuple npt) {
403 // Remove all occurrences of the node port tuple from the queue.
404 while (maintenanceQueue.remove(npt));
405 }
406
407 /**
408 * This method processes the quarantine list in bursts. The task is
409 * at most once per BDDP_TASK_INTERVAL.
410 * One each call, BDDP_TASK_SIZE number of switch ports are processed.
411 * Once the BDDP packets are sent out through the switch ports, the ports
412 * are removed from the quarantine list.
413 */
414
415 protected void processBDDPLists() {
416 int count = 0;
417 Set<NodePortTuple> nptList = new HashSet<NodePortTuple>();
418
419 while(count < BDDP_TASK_SIZE && quarantineQueue.peek() !=null) {
420 NodePortTuple npt;
421 npt = quarantineQueue.remove();
422 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
423 nptList.add(npt);
424 count++;
425 }
426
427 count = 0;
428 while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) {
429 NodePortTuple npt;
430 npt = maintenanceQueue.remove();
431 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
432 count++;
433 }
434
435 for(NodePortTuple npt:nptList) {
436 generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId());
437 }
438 }
439
440 public Set<Short> getQuarantinedPorts(long sw) {
441 Set<Short> qPorts = new HashSet<Short>();
442
443 Iterator<NodePortTuple> iter = quarantineQueue.iterator();
444 while (iter.hasNext()) {
445 NodePortTuple npt = iter.next();
446 if (npt.getNodeId() == sw) {
447 qPorts.add(npt.getPortId());
448 }
449 }
450 return qPorts;
451 }
452
453 private void generateSwitchPortStatusUpdate(long sw, short port) {
454 UpdateOperation operation;
455
456 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
457 if (iofSwitch == null) return;
458
459 OFPhysicalPort ofp = iofSwitch.getPort(port);
460 if (ofp == null) return;
461
462 int srcPortState = ofp.getState();
463 boolean portUp = ((srcPortState &
464 OFPortState.OFPPS_STP_MASK.getValue()) !=
465 OFPortState.OFPPS_STP_BLOCK.getValue());
466
467 if (portUp) operation = UpdateOperation.PORT_UP;
468 else operation = UpdateOperation.PORT_DOWN;
469
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700470 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
471
472
473 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800474 }
475
476 /**
477 * Send LLDP on known ports
478 */
479 protected void discoverOnKnownLinkPorts() {
480 // Copy the port set.
481 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
482 nptSet.addAll(portLinks.keySet());
483
484 // Send LLDP from each of them.
485 for(NodePortTuple npt: nptSet) {
486 discover(npt);
487 }
488 }
489
490 protected void discover(NodePortTuple npt) {
491 discover(npt.getNodeId(), npt.getPortId());
492 }
493
494 protected void discover(long sw, short port) {
495 sendDiscoveryMessage(sw, port, true, false);
496 }
497
498 /**
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700499 * Learn remote switches when running as a distributed controller ONOS
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800500 */
501 protected IOFSwitch addRemoteSwitch(long sw, short port) {
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -0700502 IOnosRemoteSwitch remotesw = null;
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800503
504 // add a switch if we have not seen it before
Umesh Krishnaswamy4c8e1082013-01-24 23:15:37 -0800505 remotesw = remoteSwitches.get(sw);
Jonathan Harte7231052013-01-25 00:01:14 -0800506
Pankaj Berdec125e622013-01-25 06:39:39 -0800507 if (remotesw == null) {
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800508 remotesw = new OFSwitchImpl();
509 remotesw.setupRemoteSwitch(sw);
510 remoteSwitches.put(remotesw.getId(), remotesw);
511 log.debug("addRemoteSwitch(): added fake remote sw {}", remotesw);
512 }
513
514 // add the port if we have not seen it before
Umesh Krishnaswamy68c118c2013-01-25 11:07:09 -0800515 if (remotesw.getPort(port) == null) {
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800516 OFPhysicalPort remoteport = new OFPhysicalPort();
517 remoteport.setPortNumber(port);
518 remoteport.setName("fake_" + port);
519 remoteport.setConfig(0);
520 remoteport.setState(0);
521 remotesw.setPort(remoteport);
Umesh Krishnaswamy82dcd982013-02-01 15:36:15 -0800522 log.debug("addRemoteSwitch(): added fake remote port {} to sw {}", remoteport, remotesw.getId());
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800523 }
524
525 return remotesw;
526 }
527
528 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800529 * Send link discovery message out of a given switch port.
530 * The discovery message may be a standard LLDP or a modified
531 * LLDP, where the dst mac address is set to :ff.
532 *
533 * TODO: The modified LLDP will updated in the future and may
534 * use a different eth-type.
535 * @param sw
536 * @param port
537 * @param isStandard indicates standard or modified LLDP
538 * @param isReverse indicates whether the LLDP was sent as a response
539 */
540 @LogMessageDoc(level="ERROR",
541 message="Failure sending LLDP out port {port} on switch {switch}",
542 explanation="An I/O error occured while sending LLDP message " +
543 "to the switch.",
544 recommendation=LogMessageDoc.CHECK_SWITCH)
545 protected void sendDiscoveryMessage(long sw, short port,
546 boolean isStandard,
547 boolean isReverse) {
548
549 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
550 if (iofSwitch == null) {
551 return;
552 }
553
554 if (port == OFPort.OFPP_LOCAL.getValue())
555 return;
556
557 OFPhysicalPort ofpPort = iofSwitch.getPort(port);
558
559 if (ofpPort == null) {
560 if (log.isTraceEnabled()) {
561 log.trace("Null physical port. sw={}, port={}", sw, port);
562 }
563 return;
564 }
565
566 if (isLinkDiscoverySuppressed(sw, port)) {
567 /* Dont send LLDPs out of this port as suppressLLDPs set
568 *
569 */
570 return;
571 }
572
573 // For fast ports, do not send forward LLDPs or BDDPs.
574 if (!isReverse && autoPortFastFeature && isFastPort(sw, port))
575 return;
576
577 if (log.isTraceEnabled()) {
578 log.trace("Sending LLDP packet out of swich: {}, port: {}",
579 sw, port);
580 }
581
582 // using "nearest customer bridge" MAC address for broadest possible propagation
583 // through provider and TPMR bridges (see IEEE 802.1AB-2009 and 802.1Q-2011),
584 // in particular the Linux bridge which behaves mostly like a provider bridge
585 byte[] chassisId = new byte[] {4, 0, 0, 0, 0, 0, 0}; // filled in later
586 byte[] portId = new byte[] {2, 0, 0}; // filled in later
587 byte[] ttlValue = new byte[] {0, 0x78};
588 // OpenFlow OUI - 00-26-E1
589 byte[] dpidTLVValue = new byte[] {0x0, 0x26, (byte) 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
590 LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127).setLength((short) dpidTLVValue.length).setValue(dpidTLVValue);
591
592 byte[] dpidArray = new byte[8];
593 ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
594 ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
595
596 Long dpid = sw;
597 dpidBB.putLong(dpid);
598 // set the ethernet source mac to last 6 bytes of dpid
599 System.arraycopy(dpidArray, 2, ofpPort.getHardwareAddress(), 0, 6);
600 // set the chassis id's value to last 6 bytes of dpid
601 System.arraycopy(dpidArray, 2, chassisId, 1, 6);
602 // set the optional tlv to the full dpid
603 System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
604
605
606 // set the portId to the outgoing port
607 portBB.putShort(port);
608 if (log.isTraceEnabled()) {
609 log.trace("Sending LLDP out of interface: {}/{}",
610 HexString.toHexString(sw), port);
611 }
612
613 LLDP lldp = new LLDP();
614 lldp.setChassisId(new LLDPTLV().setType((byte) 1).setLength((short) chassisId.length).setValue(chassisId));
615 lldp.setPortId(new LLDPTLV().setType((byte) 2).setLength((short) portId.length).setValue(portId));
616 lldp.setTtl(new LLDPTLV().setType((byte) 3).setLength((short) ttlValue.length).setValue(ttlValue));
617 lldp.getOptionalTLVList().add(dpidTLV);
618
619 // Add the controller identifier to the TLV value.
620 lldp.getOptionalTLVList().add(controllerTLV);
621 if (isReverse) {
622 lldp.getOptionalTLVList().add(reverseTLV);
623 }else {
624 lldp.getOptionalTLVList().add(forwardTLV);
625 }
626
627 Ethernet ethernet;
628 if (isStandard) {
629 ethernet = new Ethernet()
630 .setSourceMACAddress(ofpPort.getHardwareAddress())
631 .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
632 .setEtherType(Ethernet.TYPE_LLDP);
633 ethernet.setPayload(lldp);
634 } else {
635 BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
636 bsn.setPayload(lldp);
637
638 ethernet = new Ethernet()
639 .setSourceMACAddress(ofpPort.getHardwareAddress())
640 .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
641 .setEtherType(Ethernet.TYPE_BSN);
642 ethernet.setPayload(bsn);
643 }
644
645
646 // serialize and wrap in a packet out
647 byte[] data = ethernet.serialize();
648 OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
649 po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
650 po.setInPort(OFPort.OFPP_NONE);
651
652 // set actions
653 List<OFAction> actions = new ArrayList<OFAction>();
654 actions.add(new OFActionOutput(port, (short) 0));
655 po.setActions(actions);
656 po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
657
658 // set data
659 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + data.length);
660 po.setPacketData(data);
661
662 // send
663 try {
664 iofSwitch.write(po, null);
665 iofSwitch.flush();
666 } catch (IOException e) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800667 log.error("Failure sending LLDP out port "+port+" on switch "+iofSwitch.getStringId(), e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800668 }
669
670 }
671
672 /**
673 * Send LLDPs to all switch-ports
674 */
675 protected void discoverOnAllPorts() {
676 if (log.isTraceEnabled()) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800677 log.trace("Sending LLDP packets out of all the enabled ports on switch");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800678 }
679 Set<Long> switches = floodlightProvider.getSwitches().keySet();
680 // Send standard LLDPs
681 for (long sw: switches) {
682 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
683 if (iofSwitch == null) continue;
684 if (iofSwitch.getEnabledPorts() != null) {
685 for (OFPhysicalPort ofp: iofSwitch.getEnabledPorts()) {
686 if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber()))
687 continue;
688 if (autoPortFastFeature && isFastPort(sw, ofp.getPortNumber()))
689 continue;
690
691 // sends forward LLDP only non-fastports.
692 sendDiscoveryMessage(sw, ofp.getPortNumber(), true, false);
693
694 // If the switch port is not alreayd in the maintenance
695 // queue, add it.
696 NodePortTuple npt = new NodePortTuple(sw, ofp.getPortNumber());
697 addToMaintenanceQueue(npt);
698 }
699 }
700 }
701 }
702
703 protected void setControllerTLV() {
704 //Setting the controllerTLVValue based on current nano time,
705 //controller's IP address, and the network interface object hash
706 //the corresponding IP address.
707
708 final int prime = 7867;
709 InetAddress localIPAddress = null;
710 NetworkInterface localInterface = null;
711
712 byte[] controllerTLVValue = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; // 8 byte value.
713 ByteBuffer bb = ByteBuffer.allocate(10);
714
715 try{
716 localIPAddress = java.net.InetAddress.getLocalHost();
717 localInterface = NetworkInterface.getByInetAddress(localIPAddress);
718 } catch (Exception e) {
719 e.printStackTrace();
720 }
721
722 long result = System.nanoTime();
723 if (localIPAddress != null)
724 result = result * prime + IPv4.toIPv4Address(localIPAddress.getHostAddress());
725 if (localInterface != null)
726 result = result * prime + localInterface.hashCode();
727 // set the first 4 bits to 0.
728 result = result & (0x0fffffffffffffffL);
729
730 bb.putLong(result);
731
732 bb.rewind();
733 bb.get(controllerTLVValue, 0, 8);
734
735 this.controllerTLV = new LLDPTLV().setType((byte) 0x0c).setLength((short) controllerTLVValue.length).setValue(controllerTLVValue);
736 }
737
738 @Override
739 public String getName() {
740 return "linkdiscovery";
741 }
742
743 @Override
744 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
745 switch (msg.getType()) {
746 case PACKET_IN:
747 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx);
748 case PORT_STATUS:
749 return this.handlePortStatus(sw.getId(), (OFPortStatus) msg);
750 default:
751 break;
752 }
753 return Command.CONTINUE;
754 }
755
756 private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, boolean isStandard, FloodlightContext cntx) {
757 // If LLDP is suppressed on this port, ignore received packet as well
758 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
759 if (iofSwitch == null) {
760 return Command.STOP;
761 }
762
763 if (isLinkDiscoverySuppressed(sw, pi.getInPort()))
764 return Command.STOP;
765
766 // If this is a malformed LLDP, or not from us, exit
767 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3)
768 return Command.CONTINUE;
769
770 long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
771 long otherId = 0;
772 boolean myLLDP = false;
773 Boolean isReverse = null;
774
775 ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
776 portBB.position(1);
777
778 Short remotePort = portBB.getShort();
779 IOFSwitch remoteSwitch = null;
780
781 // Verify this LLDP packet matches what we're looking for
782 for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
783 if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 &&
784 lldptlv.getValue()[0] == 0x0 && lldptlv.getValue()[1] == 0x26 &&
785 lldptlv.getValue()[2] == (byte)0xe1 && lldptlv.getValue()[3] == 0x0) {
786 ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue());
787 remoteSwitch = floodlightProvider.getSwitches().get(dpidBB.getLong(4));
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800788 if (remoteSwitch == null) {
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700789 // Added by ONOS
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800790 // floodlight LLDP coming from a remote switch connected to a different controller
791 // add it to our cache of unconnected remote switches
792 remoteSwitch = addRemoteSwitch(dpidBB.getLong(4), remotePort);
793 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800794 } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8){
795 otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
796 if (myId == otherId)
797 myLLDP = true;
798 } else if (lldptlv.getType() == TLV_DIRECTION_TYPE &&
799 lldptlv.getLength() == TLV_DIRECTION_LENGTH) {
800 if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0])
801 isReverse = false;
802 else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0])
803 isReverse = true;
804 }
805 }
806
807 if (myLLDP == false) {
808 // This is not the LLDP sent by this controller.
809 // If the LLDP message has multicast bit set, then we need to broadcast
810 // the packet as a regular packet.
811 if (isStandard) {
812 if (log.isTraceEnabled()) {
813 log.trace("Getting standard LLDP from a different controller and quelching it.");
814 }
815 return Command.STOP;
816 }
817 else if (myId < otherId) {
818 if (log.isTraceEnabled()) {
819 log.trace("Getting BDDP packets from a different controller" +
820 "and letting it go through normal processing chain.");
821 }
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700822 //XXX ONOS: Fix the BDDP broadcast issue
Jonathan Hart0b2c76a2013-02-27 17:09:33 -0800823 //return Command.CONTINUE;
824 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800825 }
826 }
827
828
829 if (remoteSwitch == null) {
830 // Ignore LLDPs not generated by Floodlight, or from a switch that has recently
831 // disconnected, or from a switch connected to another Floodlight instance
832 if (log.isTraceEnabled()) {
833 log.trace("Received LLDP from remote switch not connected to the controller");
834 }
835 return Command.STOP;
836 }
837
838 if (!remoteSwitch.portEnabled(remotePort)) {
839 if (log.isTraceEnabled()) {
840 log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
841 }
842 return Command.STOP;
843 }
844 if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
845 remotePort))) {
846 if (log.isTraceEnabled()) {
847 log.trace("Ignoring link with suppressed src port: switch {} port {}",
848 remoteSwitch, remotePort);
849 }
850 return Command.STOP;
851 }
852 if (!iofSwitch.portEnabled(pi.getInPort())) {
853 if (log.isTraceEnabled()) {
854 log.trace("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort());
855 }
856 return Command.STOP;
857 }
858
859 OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
860 int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
861 physicalPort = iofSwitch.getPort(pi.getInPort());
862 int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
863
864 // Store the time of update to this link, and push it out to routingEngine
865 Link lt = new Link(remoteSwitch.getId(), remotePort, iofSwitch.getId(), pi.getInPort());
866
867
868 Long lastLldpTime = null;
869 Long lastBddpTime = null;
870
871 Long firstSeenTime = System.currentTimeMillis();
872
873 if (isStandard)
874 lastLldpTime = System.currentTimeMillis();
875 else
876 lastBddpTime = System.currentTimeMillis();
877
878 LinkInfo newLinkInfo =
879 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
880 srcPortState, dstPortState);
881
882 addOrUpdateLink(lt, newLinkInfo);
883
884 // Check if reverse link exists.
885 // If it doesn't exist and if the forward link was seen
886 // first seen within a small interval, send probe on the
887 // reverse link.
888
889 newLinkInfo = links.get(lt);
890 if (newLinkInfo != null && isStandard && isReverse == false) {
891 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
892 lt.getSrc(), lt.getSrcPort());
893 LinkInfo reverseInfo = links.get(reverseLink);
894 if (reverseInfo == null) {
895 // the reverse link does not exist.
896 if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - LINK_TIMEOUT) {
897 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), isStandard, true);
898 }
899 }
900 }
901
902 // If the received packet is a BDDP packet, then create a reverse BDDP
903 // link as well.
904 if (!isStandard) {
905 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
906 lt.getSrc(), lt.getSrcPort());
907
908 // srcPortState and dstPort state are reversed.
909 LinkInfo reverseInfo =
910 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
911 dstPortState, srcPortState);
912
913 addOrUpdateLink(reverseLink, reverseInfo);
914 }
915
916 // Remove the node ports from the quarantine and maintenance queues.
917 NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
918 NodePortTuple nptDst = new NodePortTuple(lt.getDst(), lt.getDstPort());
919 removeFromQuarantineQueue(nptSrc);
920 removeFromMaintenanceQueue(nptSrc);
921 removeFromQuarantineQueue(nptDst);
922 removeFromMaintenanceQueue(nptDst);
923
924 // Consume this message
925 return Command.STOP;
926 }
927
928 protected Command handlePacketIn(long sw, OFPacketIn pi,
929 FloodlightContext cntx) {
930 Ethernet eth =
931 IFloodlightProviderService.bcStore.get(cntx,
932 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
933
934 if(eth.getEtherType() == Ethernet.TYPE_BSN) {
935 BSN bsn = (BSN) eth.getPayload();
936 if (bsn == null) return Command.STOP;
937 if (bsn.getPayload() == null) return Command.STOP;
938 // It could be a packet other than BSN LLDP, therefore
939 // continue with the regular processing.
940 if (bsn.getPayload() instanceof LLDP == false)
941 return Command.CONTINUE;
Ubuntu9cbb4ca2013-02-07 17:19:59 +0000942 return handleLldp((LLDP) bsn.getPayload(), sw, pi, false, cntx);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800943 } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
944 return handleLldp((LLDP) eth.getPayload(), sw, pi, true, cntx);
945 } else if (eth.getEtherType() < 1500) {
946 long destMac = eth.getDestinationMAC().toLong();
947 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE){
948 if (log.isTraceEnabled()) {
949 log.trace("Ignoring packet addressed to 802.1D/Q " +
950 "reserved address.");
951 }
952 return Command.STOP;
953 }
954 }
955
956 // If packet-in is from a quarantine port, stop processing.
957 NodePortTuple npt = new NodePortTuple(sw, pi.getInPort());
958 if (quarantineQueue.contains(npt)) return Command.STOP;
959
960 return Command.CONTINUE;
961 }
962
963 protected UpdateOperation getUpdateOperation(int srcPortState,
964 int dstPortState) {
965 boolean added =
966 (((srcPortState &
967 OFPortState.OFPPS_STP_MASK.getValue()) !=
968 OFPortState.OFPPS_STP_BLOCK.getValue()) &&
969 ((dstPortState &
970 OFPortState.OFPPS_STP_MASK.getValue()) !=
971 OFPortState.OFPPS_STP_BLOCK.getValue()));
972
973 if (added) return UpdateOperation.LINK_UPDATED;
974 return UpdateOperation.LINK_REMOVED;
975 }
976
977
978
979 protected UpdateOperation getUpdateOperation(int srcPortState) {
980 boolean portUp = ((srcPortState &
981 OFPortState.OFPPS_STP_MASK.getValue()) !=
982 OFPortState.OFPPS_STP_BLOCK.getValue());
983
984 if (portUp) return UpdateOperation.PORT_UP;
985 else return UpdateOperation.PORT_DOWN;
986 }
987
988 protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {
989
990 NodePortTuple srcNpt, dstNpt;
991 boolean linkChanged = false;
992
993 lock.writeLock().lock();
994 try {
995 // put the new info. if an old info exists, it will be returned.
996 LinkInfo oldInfo = links.put(lt, newInfo);
997 if (oldInfo != null &&
998 oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
999 newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
1000
1001 if (log.isTraceEnabled()) {
1002 log.trace("addOrUpdateLink: {} {}",
1003 lt,
1004 (newInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast");
1005 }
1006
1007 UpdateOperation updateOperation = null;
1008 linkChanged = false;
1009
1010 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1011 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1012
1013 if (oldInfo == null) {
1014 // index it by switch source
1015 if (!switchLinks.containsKey(lt.getSrc()))
1016 switchLinks.put(lt.getSrc(), new HashSet<Link>());
1017 switchLinks.get(lt.getSrc()).add(lt);
1018
1019 // index it by switch dest
1020 if (!switchLinks.containsKey(lt.getDst()))
1021 switchLinks.put(lt.getDst(), new HashSet<Link>());
1022 switchLinks.get(lt.getDst()).add(lt);
1023
1024 // index both ends by switch:port
1025 if (!portLinks.containsKey(srcNpt))
1026 portLinks.put(srcNpt, new HashSet<Link>());
1027 portLinks.get(srcNpt).add(lt);
1028
1029 if (!portLinks.containsKey(dstNpt))
1030 portLinks.put(dstNpt, new HashSet<Link>());
1031 portLinks.get(dstNpt).add(lt);
1032
1033 // Add to portNOFLinks if the unicast valid time is null
1034 if (newInfo.getUnicastValidTime() == null)
1035 addLinkToBroadcastDomain(lt);
Umesh Krishnaswamy2b9d5642013-01-04 11:00:27 -08001036
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001037 // ONOS: Distinguish added event separately from updated event
Pankaj Berdea41016d2013-06-10 21:18:18 -07001038 updateOperation = UpdateOperation.LINK_ADDED;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001039 linkChanged = true;
1040
1041 // Add to event history
1042 evHistTopoLink(lt.getSrc(),
1043 lt.getDst(),
1044 lt.getSrcPort(),
1045 lt.getDstPort(),
1046 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1047 getLinkType(lt, newInfo),
1048 EvAction.LINK_ADDED, "LLDP Recvd");
1049 } else {
1050 // Since the link info is already there, we need to
1051 // update the right fields.
1052 if (newInfo.getUnicastValidTime() == null) {
1053 // This is due to a multicast LLDP, so copy the old unicast
1054 // value.
1055 if (oldInfo.getUnicastValidTime() != null) {
1056 newInfo.setUnicastValidTime(oldInfo.getUnicastValidTime());
1057 }
1058 } else if (newInfo.getMulticastValidTime() == null) {
1059 // This is due to a unicast LLDP, so copy the old multicast
1060 // value.
1061 if (oldInfo.getMulticastValidTime() != null) {
1062 newInfo.setMulticastValidTime(oldInfo.getMulticastValidTime());
1063 }
1064 }
1065
1066 Long oldTime = oldInfo.getUnicastValidTime();
1067 Long newTime = newInfo.getUnicastValidTime();
1068 // the link has changed its state between openflow and non-openflow
1069 // if the unicastValidTimes are null or not null
1070 if (oldTime != null & newTime == null) {
1071 // openflow -> non-openflow transition
1072 // we need to add the link tuple to the portNOFLinks
1073 addLinkToBroadcastDomain(lt);
1074 linkChanged = true;
1075 } else if (oldTime == null & newTime != null) {
1076 // non-openflow -> openflow transition
1077 // we need to remove the link from the portNOFLinks
1078 removeLinkFromBroadcastDomain(lt);
1079 linkChanged = true;
1080 }
1081
1082 // Only update the port states if they've changed
1083 if (newInfo.getSrcPortState().intValue() !=
1084 oldInfo.getSrcPortState().intValue() ||
1085 newInfo.getDstPortState().intValue() !=
1086 oldInfo.getDstPortState().intValue())
1087 linkChanged = true;
1088
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001089 if (linkChanged) {
1090 updateOperation = getUpdateOperation(newInfo.getSrcPortState(),
1091 newInfo.getDstPortState());
1092 if (log.isTraceEnabled()) {
1093 log.trace("Updated link {}", lt);
1094 }
1095 // Add to event history
1096 evHistTopoLink(lt.getSrc(),
1097 lt.getDst(),
1098 lt.getSrcPort(),
1099 lt.getDstPort(),
1100 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1101 getLinkType(lt, newInfo),
1102 EvAction.LINK_PORT_STATE_UPDATED,
1103 "LLDP Recvd");
1104 }
1105 }
1106
1107 if (linkChanged) {
1108 // find out if the link was added or removed here.
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001109 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001110 lt.getDst(), lt.getDstPort(),
1111 getLinkType(lt, newInfo),
1112 updateOperation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001113 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001114 }
1115 } finally {
1116 lock.writeLock().unlock();
1117 }
1118
1119 return linkChanged;
1120 }
1121
1122 public Map<Long, Set<Link>> getSwitchLinks() {
1123 return this.switchLinks;
1124 }
1125
1126 /**
1127 * Removes links from memory and storage.
1128 * @param links The List of @LinkTuple to delete.
1129 */
1130 protected void deleteLinks(List<Link> links, String reason) {
1131 NodePortTuple srcNpt, dstNpt;
1132
1133 lock.writeLock().lock();
1134 try {
1135 for (Link lt : links) {
1136 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1137 dstNpt =new NodePortTuple(lt.getDst(), lt.getDstPort());
1138
1139 switchLinks.get(lt.getSrc()).remove(lt);
1140 switchLinks.get(lt.getDst()).remove(lt);
1141 if (switchLinks.containsKey(lt.getSrc()) &&
1142 switchLinks.get(lt.getSrc()).isEmpty())
1143 this.switchLinks.remove(lt.getSrc());
1144 if (this.switchLinks.containsKey(lt.getDst()) &&
1145 this.switchLinks.get(lt.getDst()).isEmpty())
1146 this.switchLinks.remove(lt.getDst());
1147
1148 if (this.portLinks.get(srcNpt) != null) {
1149 this.portLinks.get(srcNpt).remove(lt);
1150 if (this.portLinks.get(srcNpt).isEmpty())
1151 this.portLinks.remove(srcNpt);
1152 }
1153 if (this.portLinks.get(dstNpt) != null) {
1154 this.portLinks.get(dstNpt).remove(lt);
1155 if (this.portLinks.get(dstNpt).isEmpty())
1156 this.portLinks.remove(dstNpt);
1157 }
1158
1159 LinkInfo info = this.links.remove(lt);
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001160 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001161 lt.getDst(), lt.getDstPort(),
1162 getLinkType(lt, info),
1163 UpdateOperation.LINK_REMOVED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001164 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001165
1166 // Update Event History
1167 evHistTopoLink(lt.getSrc(),
1168 lt.getDst(),
1169 lt.getSrcPort(),
1170 lt.getDstPort(),
1171 0, 0, // Port states
1172 ILinkDiscovery.LinkType.INVALID_LINK,
1173 EvAction.LINK_DELETED, reason);
1174
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001175 // TODO Whenever link is removed, it has to checked if
1176 // the switchports must be added to quarantine.
1177
1178 if (log.isTraceEnabled()) {
1179 log.trace("Deleted link {}", lt);
1180 }
1181 }
1182 } finally {
1183 lock.writeLock().unlock();
1184 }
1185 }
1186
1187 /**
1188 * Handles an OFPortStatus message from a switch. We will add or
1189 * delete LinkTupes as well re-compute the topology if needed.
1190 * @param sw The IOFSwitch that sent the port status message
1191 * @param ps The OFPortStatus message
1192 * @return The Command to continue or stop after we process this message
1193 */
1194 protected Command handlePortStatus(long sw, OFPortStatus ps) {
1195
1196 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
1197 if (iofSwitch == null) return Command.CONTINUE;
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -07001198
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001199 // ONOS: If we do not control this switch, then we should not process its port status messages
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001200 if (!registryService.hasControl(iofSwitch.getId())) return Command.CONTINUE;
1201
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001202 if (log.isTraceEnabled()) {
1203 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
1204 "config is {} state is {}",
1205 new Object[] {iofSwitch.getStringId(),
1206 ps.getDesc().getPortNumber(),
1207 ps.getReason(),
1208 ps.getDesc().getConfig(),
1209 ps.getDesc().getState()});
1210 }
1211
1212 short port = ps.getDesc().getPortNumber();
1213 NodePortTuple npt = new NodePortTuple(sw, port);
1214 boolean linkDeleted = false;
1215 boolean linkInfoChanged = false;
1216
1217 lock.writeLock().lock();
1218 try {
1219 // if ps is a delete, or a modify where the port is down or
1220 // configured down
1221 if ((byte)OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
1222 ((byte)OFPortReason.OFPPR_MODIFY.ordinal() ==
1223 ps.getReason() && !portEnabled(ps.getDesc()))) {
1224 deleteLinksOnPort(npt, "Port Status Changed");
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001225 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, UpdateOperation.PORT_DOWN));
1226 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001227 linkDeleted = true;
1228 }
1229 else if (ps.getReason() ==
1230 (byte)OFPortReason.OFPPR_MODIFY.ordinal()) {
1231 // If ps is a port modification and the port state has changed
1232 // that affects links in the topology
1233
1234 if (this.portLinks.containsKey(npt)) {
1235 for (Link lt: this.portLinks.get(npt)) {
1236 LinkInfo linkInfo = links.get(lt);
1237 assert(linkInfo != null);
1238 Integer updatedSrcPortState = null;
1239 Integer updatedDstPortState = null;
1240 if (lt.getSrc() == npt.getNodeId() &&
1241 lt.getSrcPort() == npt.getPortId() &&
1242 (linkInfo.getSrcPortState() !=
1243 ps.getDesc().getState())) {
1244 updatedSrcPortState = ps.getDesc().getState();
1245 linkInfo.setSrcPortState(updatedSrcPortState);
1246 }
1247 if (lt.getDst() == npt.getNodeId() &&
1248 lt.getDstPort() == npt.getPortId() &&
1249 (linkInfo.getDstPortState() !=
1250 ps.getDesc().getState())) {
1251 updatedDstPortState = ps.getDesc().getState();
1252 linkInfo.setDstPortState(updatedDstPortState);
1253 }
1254 if ((updatedSrcPortState != null) ||
1255 (updatedDstPortState != null)) {
1256 // The link is already known to link discovery
1257 // manager and the status has changed, therefore
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001258 // send an LinkUpdate.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001259 UpdateOperation operation =
1260 getUpdateOperation(linkInfo.getSrcPortState(),
1261 linkInfo.getDstPortState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001262 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001263 lt.getDst(), lt.getDstPort(),
1264 getLinkType(lt, linkInfo),
1265 operation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001266 controller.publishUpdate(update);
Jonathan Hart2fa28062013-11-25 20:16:28 -08001267
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001268 linkInfoChanged = true;
1269 }
1270 }
1271 }
1272
1273 UpdateOperation operation =
1274 getUpdateOperation(ps.getDesc().getState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001275 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
1276 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001277 }
1278
1279 if (!linkDeleted && !linkInfoChanged){
1280 if (log.isTraceEnabled()) {
1281 log.trace("handlePortStatus: Switch {} port #{} reason {};"+
1282 " no links to update/remove",
1283 new Object[] {HexString.toHexString(sw),
1284 ps.getDesc().getPortNumber(),
1285 ps.getReason()});
1286 }
1287 }
1288 } finally {
1289 lock.writeLock().unlock();
1290 }
1291
1292 if (!linkDeleted) {
1293 // Send LLDP right away when port state is changed for faster
1294 // cluster-merge. If it is a link delete then there is not need
1295 // to send the LLDPs right away and instead we wait for the LLDPs
1296 // to be sent on the timer as it is normally done
1297 // do it outside the write-lock
1298 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
1299 processNewPort(npt.getNodeId(), npt.getPortId());
1300 }
1301 return Command.CONTINUE;
1302 }
1303
1304 /**
1305 * Process a new port.
1306 * If link discovery is disabled on the port, then do nothing.
1307 * If autoportfast feature is enabled and the port is a fast port, then
1308 * do nothing.
1309 * Otherwise, send LLDP message. Add the port to quarantine.
1310 * @param sw
1311 * @param p
1312 */
1313 private void processNewPort(long sw, short p) {
1314 if (isLinkDiscoverySuppressed(sw, p)) {
1315 // Do nothing as link discovery is suppressed.
1316 }
1317 else if (autoPortFastFeature && isFastPort(sw, p)) {
1318 // Do nothing as the port is a fast port.
1319 }
1320 else {
1321 NodePortTuple npt = new NodePortTuple(sw, p);
1322 discover(sw, p);
1323 // if it is not a fast port, add it to quarantine.
1324 if (!isFastPort(sw, p)) {
1325 addToQuarantineQueue(npt);
1326 } else {
1327 // Add to maintenance queue to ensure that BDDP packets
1328 // are sent out.
1329 addToMaintenanceQueue(npt);
1330 }
1331 }
1332 }
1333
1334 /**
1335 * We send out LLDP messages when a switch is added to discover the topology
1336 * @param sw The IOFSwitch that connected to the controller
1337 */
1338 @Override
1339 public void addedSwitch(IOFSwitch sw) {
1340
1341 if (sw.getEnabledPorts() != null) {
1342 for (Short p : sw.getEnabledPortNumbers()) {
1343 processNewPort(sw.getId(), p);
1344 }
1345 }
1346 // Update event history
1347 evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001348 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), null,
1349 UpdateOperation.SWITCH_UPDATED));
1350 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001351 }
1352
1353 /**
1354 * When a switch disconnects we remove any links from our map and notify.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001355 */
1356 @Override
1357 public void removedSwitch(IOFSwitch iofSwitch) {
1358 // Update event history
1359 long sw = iofSwitch.getId();
1360 evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None");
1361 List<Link> eraseList = new ArrayList<Link>();
1362 lock.writeLock().lock();
1363 try {
1364 if (switchLinks.containsKey(sw)) {
1365 if (log.isTraceEnabled()) {
1366 log.trace("Handle switchRemoved. Switch {}; removing links {}",
1367 HexString.toHexString(sw), switchLinks.get(sw));
1368 }
1369 // add all tuples with an endpoint on this switch to erase list
1370 eraseList.addAll(switchLinks.get(sw));
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -07001371 deleteLinks(eraseList, "Switch Removed");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001372
1373 // Send a switch removed update
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001374 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED));
1375 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001376 }
1377 } finally {
1378 lock.writeLock().unlock();
1379 }
1380 }
1381
1382 /**
1383 * We don't react the port changed notifications here. we listen for
1384 * OFPortStatus messages directly. Might consider using this notifier
1385 * instead
1386 */
1387 @Override
1388 public void switchPortChanged(Long switchId) {
1389 // no-op
1390 }
1391
1392 /**
1393 * Delete links incident on a given switch port.
1394 * @param npt
1395 * @param reason
1396 */
1397 protected void deleteLinksOnPort(NodePortTuple npt, String reason) {
1398 List<Link> eraseList = new ArrayList<Link>();
1399 if (this.portLinks.containsKey(npt)) {
1400 if (log.isTraceEnabled()) {
1401 log.trace("handlePortStatus: Switch {} port #{} " +
1402 "removing links {}",
1403 new Object[] {HexString.toHexString(npt.getNodeId()),
1404 npt.getPortId(),
1405 this.portLinks.get(npt)});
1406 }
1407 eraseList.addAll(this.portLinks.get(npt));
1408 deleteLinks(eraseList, reason);
1409 }
1410 }
1411
1412 /**
1413 * Iterates through the list of links and deletes if the
1414 * last discovery message reception time exceeds timeout values.
1415 */
1416 protected void timeoutLinks() {
1417 List<Link> eraseList = new ArrayList<Link>();
1418 Long curTime = System.currentTimeMillis();
1419 boolean linkChanged = false;
1420
1421 // reentrant required here because deleteLink also write locks
1422 lock.writeLock().lock();
1423 try {
1424 Iterator<Entry<Link, LinkInfo>> it =
1425 this.links.entrySet().iterator();
1426 while (it.hasNext()) {
1427 Entry<Link, LinkInfo> entry = it.next();
1428 Link lt = entry.getKey();
1429 LinkInfo info = entry.getValue();
1430
1431 // Timeout the unicast and multicast LLDP valid times
1432 // independently.
1433 if ((info.getUnicastValidTime() != null) &&
1434 (info.getUnicastValidTime() + (this.LINK_TIMEOUT * 1000) < curTime)){
1435 info.setUnicastValidTime(null);
1436
1437 if (info.getMulticastValidTime() != null)
1438 addLinkToBroadcastDomain(lt);
1439 // Note that even if mTime becomes null later on,
1440 // the link would be deleted, which would trigger updateClusters().
1441 linkChanged = true;
1442 }
1443 if ((info.getMulticastValidTime()!= null) &&
1444 (info.getMulticastValidTime()+ (this.LINK_TIMEOUT * 1000) < curTime)) {
1445 info.setMulticastValidTime(null);
1446 // if uTime is not null, then link will remain as openflow
1447 // link. If uTime is null, it will be deleted. So, we
1448 // don't care about linkChanged flag here.
1449 removeLinkFromBroadcastDomain(lt);
1450 linkChanged = true;
1451 }
1452 // Add to the erase list only if the unicast
1453 // time is null.
1454 if (info.getUnicastValidTime() == null &&
1455 info.getMulticastValidTime() == null){
1456 eraseList.add(entry.getKey());
1457 } else if (linkChanged) {
1458 UpdateOperation operation;
1459 operation = getUpdateOperation(info.getSrcPortState(),
1460 info.getDstPortState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001461 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001462 lt.getDst(), lt.getDstPort(),
1463 getLinkType(lt, info),
1464 operation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001465 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001466 }
1467 }
1468
1469 // if any link was deleted or any link was changed.
1470 if ((eraseList.size() > 0) || linkChanged) {
1471 deleteLinks(eraseList, "LLDP timeout");
1472 }
1473 } finally {
1474 lock.writeLock().unlock();
1475 }
1476 }
1477
1478 private boolean portEnabled(OFPhysicalPort port) {
1479 if (port == null)
1480 return false;
1481 if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0)
1482 return false;
1483 if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0)
1484 return false;
1485 // Port STP state doesn't work with multiple VLANs, so ignore it for now
1486 // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
1487 // return false;
1488 return true;
1489 }
1490
1491 public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
1492 return portBroadcastDomainLinks;
1493 }
1494
1495 @Override
1496 public Map<Link, LinkInfo> getLinks() {
1497 lock.readLock().lock();
1498 Map<Link, LinkInfo> result;
1499 try {
1500 result = new HashMap<Link, LinkInfo>(links);
1501 } finally {
1502 lock.readLock().unlock();
1503 }
1504 return result;
1505 }
1506
1507 protected void addLinkToBroadcastDomain(Link lt) {
1508
1509 NodePortTuple srcNpt, dstNpt;
1510 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1511 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1512
1513 if (!portBroadcastDomainLinks.containsKey(lt.getSrc()))
1514 portBroadcastDomainLinks.put(srcNpt, new HashSet<Link>());
1515 portBroadcastDomainLinks.get(srcNpt).add(lt);
1516
1517 if (!portBroadcastDomainLinks.containsKey(lt.getDst()))
1518 portBroadcastDomainLinks.put(dstNpt, new HashSet<Link>());
1519 portBroadcastDomainLinks.get(dstNpt).add(lt);
1520 }
1521
1522 protected void removeLinkFromBroadcastDomain(Link lt) {
1523
1524 NodePortTuple srcNpt, dstNpt;
1525 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1526 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1527
1528 if (portBroadcastDomainLinks.containsKey(srcNpt)) {
1529 portBroadcastDomainLinks.get(srcNpt).remove(lt);
1530 if (portBroadcastDomainLinks.get(srcNpt).isEmpty())
1531 portBroadcastDomainLinks.remove(srcNpt);
1532 }
1533
1534 if (portBroadcastDomainLinks.containsKey(dstNpt)) {
1535 portBroadcastDomainLinks.get(dstNpt).remove(lt);
1536 if (portBroadcastDomainLinks.get(dstNpt).isEmpty())
1537 portBroadcastDomainLinks.remove(dstNpt);
1538 }
1539 }
1540
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001541 @Override
1542 public void addListener(ILinkDiscoveryListener listener) {
1543 linkDiscoveryAware.add(listener);
1544 }
1545
1546 /**
1547 * Register a link discovery aware component
1548 * @param linkDiscoveryAwareComponent
1549 */
1550 public void addLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
1551 // TODO make this a copy on write set or lock it somehow
1552 this.linkDiscoveryAware.add(linkDiscoveryAwareComponent);
1553 }
1554
1555 /**
1556 * Deregister a link discovery aware component
1557 * @param linkDiscoveryAwareComponent
1558 */
1559 public void removeLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
1560 // TODO make this a copy on write set or lock it somehow
1561 this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent);
1562 }
1563
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001564 @Override
1565 public boolean isCallbackOrderingPrereq(OFType type, String name) {
1566 return false;
1567 }
1568
1569 @Override
1570 public boolean isCallbackOrderingPostreq(OFType type, String name) {
1571 return false;
1572 }
1573
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001574 // IFloodlightModule classes
1575
1576 @Override
1577 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
1578 Collection<Class<? extends IFloodlightService>> l =
1579 new ArrayList<Class<? extends IFloodlightService>>();
1580 l.add(ILinkDiscoveryService.class);
1581 //l.add(ITopologyService.class);
1582 return l;
1583 }
1584
1585 @Override
1586 public Map<Class<? extends IFloodlightService>, IFloodlightService>
1587 getServiceImpls() {
1588 Map<Class<? extends IFloodlightService>,
1589 IFloodlightService> m =
1590 new HashMap<Class<? extends IFloodlightService>,
1591 IFloodlightService>();
1592 // We are the class that implements the service
1593 m.put(ILinkDiscoveryService.class, this);
1594 return m;
1595 }
1596
1597 @Override
1598 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
1599 Collection<Class<? extends IFloodlightService>> l =
1600 new ArrayList<Class<? extends IFloodlightService>>();
1601 l.add(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001602 l.add(IThreadPoolService.class);
1603 l.add(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001604 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001605 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001606 return l;
1607 }
1608
1609 @Override
1610 public void init(FloodlightModuleContext context)
1611 throws FloodlightModuleException {
1612 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001613 threadPool = context.getServiceImpl(IThreadPoolService.class);
1614 restApi = context.getServiceImpl(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001615 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001616 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001617
1618 // Set the autoportfast feature to false.
1619 this.autoPortFastFeature = false;
1620
1621 // We create this here because there is no ordering guarantee
1622 this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
1623 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001624 this.links = new HashMap<Link, LinkInfo>();
1625 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
1626 this.suppressLinkDiscovery =
1627 Collections.synchronizedSet(new HashSet<NodePortTuple>());
1628 this.portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
1629 this.switchLinks = new HashMap<Long, Set<Link>>();
1630 this.quarantineQueue = new LinkedBlockingQueue<NodePortTuple>();
1631 this.maintenanceQueue = new LinkedBlockingQueue<NodePortTuple>();
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001632 // Added by ONOS
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -07001633 this.remoteSwitches = new HashMap<Long, IOnosRemoteSwitch>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001634
1635 this.evHistTopologySwitch =
1636 new EventHistory<EventHistoryTopologySwitch>("Topology: Switch");
1637 this.evHistTopologyLink =
1638 new EventHistory<EventHistoryTopologyLink>("Topology: Link");
1639 this.evHistTopologyCluster =
1640 new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster");
1641 }
1642
1643 @Override
1644 @LogMessageDocs({
1645 @LogMessageDoc(level="ERROR",
1646 message="No storage source found.",
1647 explanation="Storage source was not initialized; cannot initialize " +
1648 "link discovery.",
1649 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1650 @LogMessageDoc(level="ERROR",
1651 message="Error in installing listener for " +
1652 "switch config table {table}",
1653 explanation="Failed to install storage notification for the " +
1654 "switch config table",
1655 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1656 @LogMessageDoc(level="ERROR",
1657 message="No storage source found.",
1658 explanation="Storage source was not initialized; cannot initialize " +
1659 "link discovery.",
1660 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1661 @LogMessageDoc(level="ERROR",
1662 message="Exception in LLDP send timer.",
1663 explanation="An unknown error occured while sending LLDP " +
1664 "messages to switches.",
1665 recommendation=LogMessageDoc.CHECK_SWITCH)
1666 })
1667 public void startUp(FloodlightModuleContext context) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001668 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001669 controller =
1670 context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001671
1672 // To be started by the first switch connection
1673 discoveryTask = new SingletonTask(ses, new Runnable() {
1674 @Override
1675 public void run() {
1676 try {
1677 discoverLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001678 } catch (Exception e) {
1679 log.error("Exception in LLDP send timer.", e);
1680 } finally {
1681 if (!shuttingDown) {
Jonathan Hartec4f14e2013-12-12 10:46:38 -08001682 // Always reschedule link discovery if we're not
1683 // shutting down (no chance of SLAVE role now)
1684 log.trace("Rescheduling discovery task");
1685 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
1686 TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001687 }
1688 }
1689 }
1690 });
1691
Jonathan Hartec4f14e2013-12-12 10:46:38 -08001692 // Always reschedule link discovery as we are never in SLAVE role now
1693 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001694
1695 // Setup the BDDP task. It is invoked whenever switch port tuples
1696 // are added to the quarantine list.
1697 bddpTask = new SingletonTask(ses, new QuarantineWorker());
1698 bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
1699
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001700
1701 // Register for the OpenFlow messages we want to receive
1702 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1703 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1704 // Register for switch updates
1705 floodlightProvider.addOFSwitchListener(this);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001706 if (restApi != null)
1707 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
1708 setControllerTLV();
1709 }
1710
1711 // ****************************************************
1712 // Topology Manager's Event History members and methods
1713 // ****************************************************
1714
1715 // Topology Manager event history
1716 public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch;
1717 public EventHistory<EventHistoryTopologyLink> evHistTopologyLink;
1718 public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster;
1719 public EventHistoryTopologySwitch evTopoSwitch;
1720 public EventHistoryTopologyLink evTopoLink;
1721 public EventHistoryTopologyCluster evTopoCluster;
1722
1723 // Switch Added/Deleted
1724 private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
1725 if (evTopoSwitch == null) {
1726 evTopoSwitch = new EventHistoryTopologySwitch();
1727 }
1728 evTopoSwitch.dpid = sw.getId();
1729 if ((sw.getChannel() != null) &&
1730 (SocketAddress.class.isInstance(
1731 sw.getChannel().getRemoteAddress()))) {
1732 evTopoSwitch.ipv4Addr =
1733 IPv4.toIPv4Address(((InetSocketAddress)(sw.getChannel().
1734 getRemoteAddress())).getAddress().getAddress());
1735 evTopoSwitch.l4Port =
1736 ((InetSocketAddress)(sw.getChannel().
1737 getRemoteAddress())).getPort();
1738 } else {
1739 evTopoSwitch.ipv4Addr = 0;
1740 evTopoSwitch.l4Port = 0;
1741 }
1742 evTopoSwitch.reason = reason;
1743 evTopoSwitch = evHistTopologySwitch.put(evTopoSwitch, actn);
1744 }
1745
1746 private void evHistTopoLink(long srcDpid, long dstDpid, short srcPort,
1747 short dstPort, int srcPortState, int dstPortState,
1748 ILinkDiscovery.LinkType linkType,
1749 EvAction actn, String reason) {
1750 if (evTopoLink == null) {
1751 evTopoLink = new EventHistoryTopologyLink();
1752 }
1753 evTopoLink.srcSwDpid = srcDpid;
1754 evTopoLink.dstSwDpid = dstDpid;
1755 evTopoLink.srcSwport = srcPort & 0xffff;
1756 evTopoLink.dstSwport = dstPort & 0xffff;
1757 evTopoLink.srcPortState = srcPortState;
1758 evTopoLink.dstPortState = dstPortState;
1759 evTopoLink.reason = reason;
1760 switch (linkType) {
1761 case DIRECT_LINK:
1762 evTopoLink.linkType = "DIRECT_LINK";
1763 break;
1764 case MULTIHOP_LINK:
1765 evTopoLink.linkType = "MULTIHOP_LINK";
1766 break;
1767 case TUNNEL:
1768 evTopoLink.linkType = "TUNNEL";
1769 break;
1770 case INVALID_LINK:
1771 default:
1772 evTopoLink.linkType = "Unknown";
1773 break;
1774 }
1775 evTopoLink = evHistTopologyLink.put(evTopoLink, actn);
1776 }
1777
1778 public void evHistTopoCluster(long dpid, long clusterIdOld,
1779 long clusterIdNew, EvAction action, String reason) {
1780 if (evTopoCluster == null) {
1781 evTopoCluster = new EventHistoryTopologyCluster();
1782 }
1783 evTopoCluster.dpid = dpid;
1784 evTopoCluster.clusterIdOld = clusterIdOld;
1785 evTopoCluster.clusterIdNew = clusterIdNew;
1786 evTopoCluster.reason = reason;
1787 evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action);
1788 }
1789
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001790 public boolean isAutoPortFastFeature() {
1791 return autoPortFastFeature;
1792 }
1793
1794 public void setAutoPortFastFeature(boolean autoPortFastFeature) {
1795 this.autoPortFastFeature = autoPortFastFeature;
1796 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001797}