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