blob: a035867bc0e48232ba8b5c19a1bbea5e74a87907 [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;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080061import net.floodlightcontroller.threadpool.IThreadPoolService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080062import net.floodlightcontroller.util.EventHistory;
63import net.floodlightcontroller.util.EventHistory.EvAction;
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -070064import net.onrc.onos.ofcontroller.core.IOnosRemoteSwitch;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070065import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery;
Jonathan Hart2fa28062013-11-25 20:16:28 -080066import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
67import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.UpdateOperation;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070068import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryListener;
69import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
Jonathan Hart02a59e42014-03-26 18:50:23 -070070import net.onrc.onos.ofcontroller.linkdiscovery.Link;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070071import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
Jonathan Hart02a59e42014-03-26 18:50:23 -070072import net.onrc.onos.ofcontroller.linkdiscovery.NodePortTuple;
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
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800115implements 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
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800128 private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800129 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
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800138 // Direction TLVs are used to indicate if the LLDPs were sent
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800139 // 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};
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800144 private static final LLDPTLV forwardTLV
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800145 = new LLDPTLV().
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800146 setType(TLV_DIRECTION_TYPE).
147 setLength(TLV_DIRECTION_LENGTH).
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800148 setValue(TLV_DIRECTION_VALUE_FORWARD);
149
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800150 private static final LLDPTLV reverseTLV
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800151 = new LLDPTLV().
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800152 setType(TLV_DIRECTION_TYPE).
153 setLength(TLV_DIRECTION_LENGTH).
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800154 setValue(TLV_DIRECTION_VALUE_REVERSE);
155
156 // Link discovery task details.
157 protected SingletonTask discoveryTask;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800158 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;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800209
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700210 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
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800266 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800267 * 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
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800286 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800287 public Set<NodePortTuple> getSuppressLLDPsInfo() {
288 return suppressLinkDiscovery;
289 }
290
291 /**
292 * Add a switch port to the suppressed LLDP list.
293 * Remove any known links on the switch port.
294 */
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800295 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800296 public void AddToSuppressLLDPs(long sw, short port)
297 {
298 NodePortTuple npt = new NodePortTuple(sw, port);
299 this.suppressLinkDiscovery.add(npt);
300 deleteLinksOnPort(npt, "LLDP suppressed.");
301 }
302
303 /**
304 * Remove a switch port from the suppressed LLDP list.
305 * Discover links on that switchport.
306 */
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800307 @Override
308 public void RemoveFromSuppressLLDPs(long sw, short port)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800309 {
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
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800323 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800324 public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
325 if (info.getUnicastValidTime() != null) {
326 return ILinkDiscovery.LinkType.DIRECT_LINK;
327 } else if (info.getMulticastValidTime() != null) {
328 return ILinkDiscovery.LinkType.MULTIHOP_LINK;
329 }
330 return ILinkDiscovery.LinkType.INVALID_LINK;
331 }
332
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800333
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800334 private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
335 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
336 }
337
338 protected void discoverLinks() {
339
340 // timeout known links.
341 timeoutLinks();
342
343 //increment LLDP clock
344 lldpClock = (lldpClock + 1)% LLDP_TO_ALL_INTERVAL;
345
346 if (lldpClock == 0) {
347 log.debug("Sending LLDP out on all ports.");
348 discoverOnAllPorts();
349 }
350 }
351
352
353 /**
354 * Quarantine Ports.
355 */
356 protected class QuarantineWorker implements Runnable {
357 @Override
358 public void run() {
359 try {
360 processBDDPLists();
361 }
362 catch (Exception e) {
363 log.error("Error in quarantine worker thread", e);
364 } finally {
365 bddpTask.reschedule(BDDP_TASK_INTERVAL,
366 TimeUnit.MILLISECONDS);
367 }
368 }
369 }
370
371 /**
372 * Add a switch port to the quarantine queue. Schedule the
373 * quarantine task if the quarantine queue was empty before adding
374 * this switch port.
375 * @param npt
376 */
377 protected void addToQuarantineQueue(NodePortTuple npt) {
378 if (quarantineQueue.contains(npt) == false)
379 quarantineQueue.add(npt);
380 }
381
382 /**
383 * Remove a switch port from the quarantine queue.
384 */
385 protected void removeFromQuarantineQueue(NodePortTuple npt) {
386 // Remove all occurrences of the node port tuple from the list.
387 while (quarantineQueue.remove(npt));
388 }
389
390 /**
391 * Add a switch port to maintenance queue.
392 * @param npt
393 */
394 protected void addToMaintenanceQueue(NodePortTuple npt) {
395 // TODO We are not checking if the switch port tuple is already
396 // in the maintenance list or not. This will be an issue for
397 // really large number of switch ports in the network.
398 if (maintenanceQueue.contains(npt) == false)
399 maintenanceQueue.add(npt);
400 }
401
402 /**
403 * Remove a switch port from maintenance queue.
404 * @param npt
405 */
406 protected void removeFromMaintenanceQueue(NodePortTuple npt) {
407 // Remove all occurrences of the node port tuple from the queue.
408 while (maintenanceQueue.remove(npt));
409 }
410
411 /**
412 * This method processes the quarantine list in bursts. The task is
413 * at most once per BDDP_TASK_INTERVAL.
414 * One each call, BDDP_TASK_SIZE number of switch ports are processed.
415 * Once the BDDP packets are sent out through the switch ports, the ports
416 * are removed from the quarantine list.
417 */
418
419 protected void processBDDPLists() {
420 int count = 0;
421 Set<NodePortTuple> nptList = new HashSet<NodePortTuple>();
422
423 while(count < BDDP_TASK_SIZE && quarantineQueue.peek() !=null) {
424 NodePortTuple npt;
425 npt = quarantineQueue.remove();
426 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
427 nptList.add(npt);
428 count++;
429 }
430
431 count = 0;
432 while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) {
433 NodePortTuple npt;
434 npt = maintenanceQueue.remove();
435 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
436 count++;
437 }
438
439 for(NodePortTuple npt:nptList) {
440 generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId());
441 }
442 }
443
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800444 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800445 public Set<Short> getQuarantinedPorts(long sw) {
446 Set<Short> qPorts = new HashSet<Short>();
447
448 Iterator<NodePortTuple> iter = quarantineQueue.iterator();
449 while (iter.hasNext()) {
450 NodePortTuple npt = iter.next();
451 if (npt.getNodeId() == sw) {
452 qPorts.add(npt.getPortId());
453 }
454 }
455 return qPorts;
456 }
457
458 private void generateSwitchPortStatusUpdate(long sw, short port) {
459 UpdateOperation operation;
460
461 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
462 if (iofSwitch == null) return;
463
464 OFPhysicalPort ofp = iofSwitch.getPort(port);
465 if (ofp == null) return;
466
467 int srcPortState = ofp.getState();
468 boolean portUp = ((srcPortState &
469 OFPortState.OFPPS_STP_MASK.getValue()) !=
470 OFPortState.OFPPS_STP_BLOCK.getValue());
471
472 if (portUp) operation = UpdateOperation.PORT_UP;
473 else operation = UpdateOperation.PORT_DOWN;
474
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700475 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800476
477
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700478 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800479 }
480
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800481 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800482 * Send LLDP on known ports
483 */
484 protected void discoverOnKnownLinkPorts() {
485 // Copy the port set.
486 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
487 nptSet.addAll(portLinks.keySet());
488
489 // Send LLDP from each of them.
490 for(NodePortTuple npt: nptSet) {
491 discover(npt);
492 }
493 }
494
495 protected void discover(NodePortTuple npt) {
496 discover(npt.getNodeId(), npt.getPortId());
497 }
498
499 protected void discover(long sw, short port) {
500 sendDiscoveryMessage(sw, port, true, false);
501 }
502
503 /**
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700504 * Learn remote switches when running as a distributed controller ONOS
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800505 */
506 protected IOFSwitch addRemoteSwitch(long sw, short port) {
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -0700507 IOnosRemoteSwitch remotesw = null;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800508
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800509 // add a switch if we have not seen it before
Umesh Krishnaswamy4c8e1082013-01-24 23:15:37 -0800510 remotesw = remoteSwitches.get(sw);
Jonathan Harte7231052013-01-25 00:01:14 -0800511
Pankaj Berdec125e622013-01-25 06:39:39 -0800512 if (remotesw == null) {
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800513 remotesw = new OFSwitchImpl();
514 remotesw.setupRemoteSwitch(sw);
515 remoteSwitches.put(remotesw.getId(), remotesw);
516 log.debug("addRemoteSwitch(): added fake remote sw {}", remotesw);
517 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800518
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800519 // add the port if we have not seen it before
Umesh Krishnaswamy68c118c2013-01-25 11:07:09 -0800520 if (remotesw.getPort(port) == null) {
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800521 OFPhysicalPort remoteport = new OFPhysicalPort();
522 remoteport.setPortNumber(port);
523 remoteport.setName("fake_" + port);
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800524 remoteport.setConfig(0);
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800525 remoteport.setState(0);
526 remotesw.setPort(remoteport);
Umesh Krishnaswamy82dcd982013-02-01 15:36:15 -0800527 log.debug("addRemoteSwitch(): added fake remote port {} to sw {}", remoteport, remotesw.getId());
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800528 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800529
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800530 return remotesw;
531 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800532
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800533 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800534 * Send link discovery message out of a given switch port.
535 * The discovery message may be a standard LLDP or a modified
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800536 * LLDP, where the dst mac address is set to :ff.
537 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800538 * TODO: The modified LLDP will updated in the future and may
539 * use a different eth-type.
540 * @param sw
541 * @param port
542 * @param isStandard indicates standard or modified LLDP
543 * @param isReverse indicates whether the LLDP was sent as a response
544 */
545 @LogMessageDoc(level="ERROR",
546 message="Failure sending LLDP out port {port} on switch {switch}",
547 explanation="An I/O error occured while sending LLDP message " +
548 "to the switch.",
549 recommendation=LogMessageDoc.CHECK_SWITCH)
550 protected void sendDiscoveryMessage(long sw, short port,
551 boolean isStandard,
552 boolean isReverse) {
553
554 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
555 if (iofSwitch == null) {
556 return;
557 }
558
559 if (port == OFPort.OFPP_LOCAL.getValue())
560 return;
561
562 OFPhysicalPort ofpPort = iofSwitch.getPort(port);
563
564 if (ofpPort == null) {
565 if (log.isTraceEnabled()) {
566 log.trace("Null physical port. sw={}, port={}", sw, port);
567 }
568 return;
569 }
570
571 if (isLinkDiscoverySuppressed(sw, port)) {
572 /* Dont send LLDPs out of this port as suppressLLDPs set
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800573 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800574 */
575 return;
576 }
577
578 // For fast ports, do not send forward LLDPs or BDDPs.
579 if (!isReverse && autoPortFastFeature && isFastPort(sw, port))
580 return;
581
582 if (log.isTraceEnabled()) {
583 log.trace("Sending LLDP packet out of swich: {}, port: {}",
584 sw, port);
585 }
586
587 // using "nearest customer bridge" MAC address for broadest possible propagation
588 // through provider and TPMR bridges (see IEEE 802.1AB-2009 and 802.1Q-2011),
589 // in particular the Linux bridge which behaves mostly like a provider bridge
590 byte[] chassisId = new byte[] {4, 0, 0, 0, 0, 0, 0}; // filled in later
591 byte[] portId = new byte[] {2, 0, 0}; // filled in later
592 byte[] ttlValue = new byte[] {0, 0x78};
593 // OpenFlow OUI - 00-26-E1
594 byte[] dpidTLVValue = new byte[] {0x0, 0x26, (byte) 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
595 LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127).setLength((short) dpidTLVValue.length).setValue(dpidTLVValue);
596
597 byte[] dpidArray = new byte[8];
598 ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
599 ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
600
601 Long dpid = sw;
602 dpidBB.putLong(dpid);
603 // set the ethernet source mac to last 6 bytes of dpid
604 System.arraycopy(dpidArray, 2, ofpPort.getHardwareAddress(), 0, 6);
605 // set the chassis id's value to last 6 bytes of dpid
606 System.arraycopy(dpidArray, 2, chassisId, 1, 6);
607 // set the optional tlv to the full dpid
608 System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
609
610
611 // set the portId to the outgoing port
612 portBB.putShort(port);
613 if (log.isTraceEnabled()) {
614 log.trace("Sending LLDP out of interface: {}/{}",
615 HexString.toHexString(sw), port);
616 }
617
618 LLDP lldp = new LLDP();
619 lldp.setChassisId(new LLDPTLV().setType((byte) 1).setLength((short) chassisId.length).setValue(chassisId));
620 lldp.setPortId(new LLDPTLV().setType((byte) 2).setLength((short) portId.length).setValue(portId));
621 lldp.setTtl(new LLDPTLV().setType((byte) 3).setLength((short) ttlValue.length).setValue(ttlValue));
622 lldp.getOptionalTLVList().add(dpidTLV);
623
624 // Add the controller identifier to the TLV value.
625 lldp.getOptionalTLVList().add(controllerTLV);
626 if (isReverse) {
627 lldp.getOptionalTLVList().add(reverseTLV);
628 }else {
629 lldp.getOptionalTLVList().add(forwardTLV);
630 }
631
632 Ethernet ethernet;
633 if (isStandard) {
634 ethernet = new Ethernet()
635 .setSourceMACAddress(ofpPort.getHardwareAddress())
636 .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
637 .setEtherType(Ethernet.TYPE_LLDP);
638 ethernet.setPayload(lldp);
639 } else {
640 BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
641 bsn.setPayload(lldp);
642
643 ethernet = new Ethernet()
644 .setSourceMACAddress(ofpPort.getHardwareAddress())
645 .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
646 .setEtherType(Ethernet.TYPE_BSN);
647 ethernet.setPayload(bsn);
648 }
649
650
651 // serialize and wrap in a packet out
652 byte[] data = ethernet.serialize();
653 OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
654 po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
655 po.setInPort(OFPort.OFPP_NONE);
656
657 // set actions
658 List<OFAction> actions = new ArrayList<OFAction>();
659 actions.add(new OFActionOutput(port, (short) 0));
660 po.setActions(actions);
661 po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
662
663 // set data
664 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + data.length);
665 po.setPacketData(data);
666
667 // send
668 try {
669 iofSwitch.write(po, null);
670 iofSwitch.flush();
671 } catch (IOException e) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800672 log.error("Failure sending LLDP out port "+port+" on switch "+iofSwitch.getStringId(), e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800673 }
674
675 }
676
677 /**
678 * Send LLDPs to all switch-ports
679 */
680 protected void discoverOnAllPorts() {
681 if (log.isTraceEnabled()) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800682 log.trace("Sending LLDP packets out of all the enabled ports on switch");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800683 }
684 Set<Long> switches = floodlightProvider.getSwitches().keySet();
685 // Send standard LLDPs
686 for (long sw: switches) {
687 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
688 if (iofSwitch == null) continue;
689 if (iofSwitch.getEnabledPorts() != null) {
690 for (OFPhysicalPort ofp: iofSwitch.getEnabledPorts()) {
691 if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber()))
692 continue;
693 if (autoPortFastFeature && isFastPort(sw, ofp.getPortNumber()))
694 continue;
695
696 // sends forward LLDP only non-fastports.
697 sendDiscoveryMessage(sw, ofp.getPortNumber(), true, false);
698
699 // If the switch port is not alreayd in the maintenance
700 // queue, add it.
701 NodePortTuple npt = new NodePortTuple(sw, ofp.getPortNumber());
702 addToMaintenanceQueue(npt);
703 }
704 }
705 }
706 }
707
708 protected void setControllerTLV() {
709 //Setting the controllerTLVValue based on current nano time,
710 //controller's IP address, and the network interface object hash
711 //the corresponding IP address.
712
713 final int prime = 7867;
714 InetAddress localIPAddress = null;
715 NetworkInterface localInterface = null;
716
717 byte[] controllerTLVValue = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; // 8 byte value.
718 ByteBuffer bb = ByteBuffer.allocate(10);
719
720 try{
721 localIPAddress = java.net.InetAddress.getLocalHost();
722 localInterface = NetworkInterface.getByInetAddress(localIPAddress);
723 } catch (Exception e) {
724 e.printStackTrace();
725 }
726
727 long result = System.nanoTime();
728 if (localIPAddress != null)
729 result = result * prime + IPv4.toIPv4Address(localIPAddress.getHostAddress());
730 if (localInterface != null)
731 result = result * prime + localInterface.hashCode();
732 // set the first 4 bits to 0.
733 result = result & (0x0fffffffffffffffL);
734
735 bb.putLong(result);
736
737 bb.rewind();
738 bb.get(controllerTLVValue, 0, 8);
739
740 this.controllerTLV = new LLDPTLV().setType((byte) 0x0c).setLength((short) controllerTLVValue.length).setValue(controllerTLVValue);
741 }
742
743 @Override
744 public String getName() {
745 return "linkdiscovery";
746 }
747
748 @Override
749 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
750 switch (msg.getType()) {
751 case PACKET_IN:
752 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx);
753 case PORT_STATUS:
754 return this.handlePortStatus(sw.getId(), (OFPortStatus) msg);
755 default:
756 break;
757 }
758 return Command.CONTINUE;
759 }
760
761 private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, boolean isStandard, FloodlightContext cntx) {
762 // If LLDP is suppressed on this port, ignore received packet as well
763 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
764 if (iofSwitch == null) {
765 return Command.STOP;
766 }
767
768 if (isLinkDiscoverySuppressed(sw, pi.getInPort()))
769 return Command.STOP;
770
771 // If this is a malformed LLDP, or not from us, exit
772 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3)
773 return Command.CONTINUE;
774
775 long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
776 long otherId = 0;
777 boolean myLLDP = false;
778 Boolean isReverse = null;
779
780 ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
781 portBB.position(1);
782
783 Short remotePort = portBB.getShort();
784 IOFSwitch remoteSwitch = null;
785
786 // Verify this LLDP packet matches what we're looking for
787 for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
788 if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 &&
789 lldptlv.getValue()[0] == 0x0 && lldptlv.getValue()[1] == 0x26 &&
790 lldptlv.getValue()[2] == (byte)0xe1 && lldptlv.getValue()[3] == 0x0) {
791 ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue());
792 remoteSwitch = floodlightProvider.getSwitches().get(dpidBB.getLong(4));
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800793 if (remoteSwitch == null) {
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700794 // Added by ONOS
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800795 // floodlight LLDP coming from a remote switch connected to a different controller
796 // add it to our cache of unconnected remote switches
797 remoteSwitch = addRemoteSwitch(dpidBB.getLong(4), remotePort);
798 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800799 } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8){
800 otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
801 if (myId == otherId)
802 myLLDP = true;
803 } else if (lldptlv.getType() == TLV_DIRECTION_TYPE &&
804 lldptlv.getLength() == TLV_DIRECTION_LENGTH) {
805 if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0])
806 isReverse = false;
807 else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0])
808 isReverse = true;
809 }
810 }
811
812 if (myLLDP == false) {
813 // This is not the LLDP sent by this controller.
814 // If the LLDP message has multicast bit set, then we need to broadcast
815 // the packet as a regular packet.
816 if (isStandard) {
817 if (log.isTraceEnabled()) {
818 log.trace("Getting standard LLDP from a different controller and quelching it.");
819 }
820 return Command.STOP;
821 }
Teru Ub7246af2014-01-13 13:24:38 -0800822 else if(sw <= remoteSwitch.getId()){
823 if (log.isTraceEnabled()) {
824 log.trace("Getting BBDP from a different controller. myId {}: remoteId {}", myId, otherId);
825 log.trace("and my controller id is smaller than the other, so quelching it. myPort {}: rPort {}", pi.getInPort(), remotePort);
826 }
827 //XXX ONOS: Fix the BDDP broadcast issue
828 //return Command.CONTINUE;
829 return Command.STOP;
830 }
831 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800832 else if (myId < otherId) {
833 if (log.isTraceEnabled()) {
834 log.trace("Getting BDDP packets from a different controller" +
835 "and letting it go through normal processing chain.");
836 }
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700837 //XXX ONOS: Fix the BDDP broadcast issue
Jonathan Hart0b2c76a2013-02-27 17:09:33 -0800838 //return Command.CONTINUE;
839 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800840 }
Teru Ub7246af2014-01-13 13:24:38 -0800841 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800842 }
843
844
845 if (remoteSwitch == null) {
846 // Ignore LLDPs not generated by Floodlight, or from a switch that has recently
847 // disconnected, or from a switch connected to another Floodlight instance
848 if (log.isTraceEnabled()) {
849 log.trace("Received LLDP from remote switch not connected to the controller");
850 }
851 return Command.STOP;
852 }
853
854 if (!remoteSwitch.portEnabled(remotePort)) {
855 if (log.isTraceEnabled()) {
856 log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
857 }
858 return Command.STOP;
859 }
860 if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
861 remotePort))) {
862 if (log.isTraceEnabled()) {
863 log.trace("Ignoring link with suppressed src port: switch {} port {}",
864 remoteSwitch, remotePort);
865 }
866 return Command.STOP;
867 }
868 if (!iofSwitch.portEnabled(pi.getInPort())) {
869 if (log.isTraceEnabled()) {
870 log.trace("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort());
871 }
872 return Command.STOP;
873 }
874
875 OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
876 int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
877 physicalPort = iofSwitch.getPort(pi.getInPort());
878 int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
879
880 // Store the time of update to this link, and push it out to routingEngine
881 Link lt = new Link(remoteSwitch.getId(), remotePort, iofSwitch.getId(), pi.getInPort());
882
883
884 Long lastLldpTime = null;
885 Long lastBddpTime = null;
886
887 Long firstSeenTime = System.currentTimeMillis();
888
889 if (isStandard)
890 lastLldpTime = System.currentTimeMillis();
891 else
892 lastBddpTime = System.currentTimeMillis();
893
894 LinkInfo newLinkInfo =
895 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
896 srcPortState, dstPortState);
897
898 addOrUpdateLink(lt, newLinkInfo);
899
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800900 // Check if reverse link exists.
901 // If it doesn't exist and if the forward link was seen
902 // first seen within a small interval, send probe on the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800903 // reverse link.
904
905 newLinkInfo = links.get(lt);
906 if (newLinkInfo != null && isStandard && isReverse == false) {
907 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
908 lt.getSrc(), lt.getSrcPort());
909 LinkInfo reverseInfo = links.get(reverseLink);
910 if (reverseInfo == null) {
911 // the reverse link does not exist.
912 if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - LINK_TIMEOUT) {
913 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), isStandard, true);
914 }
915 }
916 }
917
918 // If the received packet is a BDDP packet, then create a reverse BDDP
919 // link as well.
920 if (!isStandard) {
921 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
922 lt.getSrc(), lt.getSrcPort());
923
924 // srcPortState and dstPort state are reversed.
925 LinkInfo reverseInfo =
926 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
927 dstPortState, srcPortState);
928
929 addOrUpdateLink(reverseLink, reverseInfo);
930 }
931
932 // Remove the node ports from the quarantine and maintenance queues.
933 NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
934 NodePortTuple nptDst = new NodePortTuple(lt.getDst(), lt.getDstPort());
935 removeFromQuarantineQueue(nptSrc);
936 removeFromMaintenanceQueue(nptSrc);
937 removeFromQuarantineQueue(nptDst);
938 removeFromMaintenanceQueue(nptDst);
939
940 // Consume this message
941 return Command.STOP;
942 }
943
944 protected Command handlePacketIn(long sw, OFPacketIn pi,
945 FloodlightContext cntx) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800946 Ethernet eth =
947 IFloodlightProviderService.bcStore.get(cntx,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800948 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
949
950 if(eth.getEtherType() == Ethernet.TYPE_BSN) {
951 BSN bsn = (BSN) eth.getPayload();
952 if (bsn == null) return Command.STOP;
953 if (bsn.getPayload() == null) return Command.STOP;
954 // It could be a packet other than BSN LLDP, therefore
955 // continue with the regular processing.
956 if (bsn.getPayload() instanceof LLDP == false)
957 return Command.CONTINUE;
Ubuntu9cbb4ca2013-02-07 17:19:59 +0000958 return handleLldp((LLDP) bsn.getPayload(), sw, pi, false, cntx);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800959 } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
960 return handleLldp((LLDP) eth.getPayload(), sw, pi, true, cntx);
961 } else if (eth.getEtherType() < 1500) {
962 long destMac = eth.getDestinationMAC().toLong();
963 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE){
964 if (log.isTraceEnabled()) {
965 log.trace("Ignoring packet addressed to 802.1D/Q " +
966 "reserved address.");
967 }
968 return Command.STOP;
969 }
970 }
971
972 // If packet-in is from a quarantine port, stop processing.
973 NodePortTuple npt = new NodePortTuple(sw, pi.getInPort());
974 if (quarantineQueue.contains(npt)) return Command.STOP;
975
976 return Command.CONTINUE;
977 }
978
979 protected UpdateOperation getUpdateOperation(int srcPortState,
980 int dstPortState) {
981 boolean added =
982 (((srcPortState &
983 OFPortState.OFPPS_STP_MASK.getValue()) !=
984 OFPortState.OFPPS_STP_BLOCK.getValue()) &&
985 ((dstPortState &
986 OFPortState.OFPPS_STP_MASK.getValue()) !=
987 OFPortState.OFPPS_STP_BLOCK.getValue()));
988
989 if (added) return UpdateOperation.LINK_UPDATED;
990 return UpdateOperation.LINK_REMOVED;
991 }
992
993
994
995 protected UpdateOperation getUpdateOperation(int srcPortState) {
996 boolean portUp = ((srcPortState &
997 OFPortState.OFPPS_STP_MASK.getValue()) !=
998 OFPortState.OFPPS_STP_BLOCK.getValue());
999
1000 if (portUp) return UpdateOperation.PORT_UP;
1001 else return UpdateOperation.PORT_DOWN;
1002 }
1003
1004 protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {
1005
1006 NodePortTuple srcNpt, dstNpt;
1007 boolean linkChanged = false;
1008
1009 lock.writeLock().lock();
1010 try {
1011 // put the new info. if an old info exists, it will be returned.
1012 LinkInfo oldInfo = links.put(lt, newInfo);
1013 if (oldInfo != null &&
1014 oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
1015 newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
1016
1017 if (log.isTraceEnabled()) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001018 log.trace("addOrUpdateLink: {} {}",
1019 lt,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001020 (newInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast");
1021 }
1022
1023 UpdateOperation updateOperation = null;
1024 linkChanged = false;
1025
1026 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1027 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1028
1029 if (oldInfo == null) {
1030 // index it by switch source
1031 if (!switchLinks.containsKey(lt.getSrc()))
1032 switchLinks.put(lt.getSrc(), new HashSet<Link>());
1033 switchLinks.get(lt.getSrc()).add(lt);
1034
1035 // index it by switch dest
1036 if (!switchLinks.containsKey(lt.getDst()))
1037 switchLinks.put(lt.getDst(), new HashSet<Link>());
1038 switchLinks.get(lt.getDst()).add(lt);
1039
1040 // index both ends by switch:port
1041 if (!portLinks.containsKey(srcNpt))
1042 portLinks.put(srcNpt, new HashSet<Link>());
1043 portLinks.get(srcNpt).add(lt);
1044
1045 if (!portLinks.containsKey(dstNpt))
1046 portLinks.put(dstNpt, new HashSet<Link>());
1047 portLinks.get(dstNpt).add(lt);
1048
1049 // Add to portNOFLinks if the unicast valid time is null
1050 if (newInfo.getUnicastValidTime() == null)
1051 addLinkToBroadcastDomain(lt);
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001052
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001053 // ONOS: Distinguish added event separately from updated event
Pankaj Berdea41016d2013-06-10 21:18:18 -07001054 updateOperation = UpdateOperation.LINK_ADDED;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001055 linkChanged = true;
1056
1057 // Add to event history
1058 evHistTopoLink(lt.getSrc(),
1059 lt.getDst(),
1060 lt.getSrcPort(),
1061 lt.getDstPort(),
1062 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1063 getLinkType(lt, newInfo),
1064 EvAction.LINK_ADDED, "LLDP Recvd");
1065 } else {
1066 // Since the link info is already there, we need to
1067 // update the right fields.
1068 if (newInfo.getUnicastValidTime() == null) {
1069 // This is due to a multicast LLDP, so copy the old unicast
1070 // value.
1071 if (oldInfo.getUnicastValidTime() != null) {
1072 newInfo.setUnicastValidTime(oldInfo.getUnicastValidTime());
1073 }
1074 } else if (newInfo.getMulticastValidTime() == null) {
1075 // This is due to a unicast LLDP, so copy the old multicast
1076 // value.
1077 if (oldInfo.getMulticastValidTime() != null) {
1078 newInfo.setMulticastValidTime(oldInfo.getMulticastValidTime());
1079 }
1080 }
1081
1082 Long oldTime = oldInfo.getUnicastValidTime();
1083 Long newTime = newInfo.getUnicastValidTime();
1084 // the link has changed its state between openflow and non-openflow
1085 // if the unicastValidTimes are null or not null
1086 if (oldTime != null & newTime == null) {
1087 // openflow -> non-openflow transition
1088 // we need to add the link tuple to the portNOFLinks
1089 addLinkToBroadcastDomain(lt);
1090 linkChanged = true;
1091 } else if (oldTime == null & newTime != null) {
1092 // non-openflow -> openflow transition
1093 // we need to remove the link from the portNOFLinks
1094 removeLinkFromBroadcastDomain(lt);
1095 linkChanged = true;
1096 }
1097
1098 // Only update the port states if they've changed
1099 if (newInfo.getSrcPortState().intValue() !=
1100 oldInfo.getSrcPortState().intValue() ||
1101 newInfo.getDstPortState().intValue() !=
1102 oldInfo.getDstPortState().intValue())
1103 linkChanged = true;
1104
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001105 if (linkChanged) {
1106 updateOperation = getUpdateOperation(newInfo.getSrcPortState(),
1107 newInfo.getDstPortState());
1108 if (log.isTraceEnabled()) {
1109 log.trace("Updated link {}", lt);
1110 }
1111 // Add to event history
1112 evHistTopoLink(lt.getSrc(),
1113 lt.getDst(),
1114 lt.getSrcPort(),
1115 lt.getDstPort(),
1116 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1117 getLinkType(lt, newInfo),
1118 EvAction.LINK_PORT_STATE_UPDATED,
1119 "LLDP Recvd");
1120 }
1121 }
1122
1123 if (linkChanged) {
1124 // find out if the link was added or removed here.
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001125 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001126 lt.getDst(), lt.getDstPort(),
1127 getLinkType(lt, newInfo),
1128 updateOperation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001129 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001130 }
1131 } finally {
1132 lock.writeLock().unlock();
1133 }
1134
1135 return linkChanged;
1136 }
1137
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001138 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001139 public Map<Long, Set<Link>> getSwitchLinks() {
1140 return this.switchLinks;
1141 }
1142
1143 /**
1144 * Removes links from memory and storage.
1145 * @param links The List of @LinkTuple to delete.
1146 */
1147 protected void deleteLinks(List<Link> links, String reason) {
1148 NodePortTuple srcNpt, dstNpt;
1149
1150 lock.writeLock().lock();
1151 try {
1152 for (Link lt : links) {
1153 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1154 dstNpt =new NodePortTuple(lt.getDst(), lt.getDstPort());
1155
1156 switchLinks.get(lt.getSrc()).remove(lt);
1157 switchLinks.get(lt.getDst()).remove(lt);
1158 if (switchLinks.containsKey(lt.getSrc()) &&
1159 switchLinks.get(lt.getSrc()).isEmpty())
1160 this.switchLinks.remove(lt.getSrc());
1161 if (this.switchLinks.containsKey(lt.getDst()) &&
1162 this.switchLinks.get(lt.getDst()).isEmpty())
1163 this.switchLinks.remove(lt.getDst());
1164
1165 if (this.portLinks.get(srcNpt) != null) {
1166 this.portLinks.get(srcNpt).remove(lt);
1167 if (this.portLinks.get(srcNpt).isEmpty())
1168 this.portLinks.remove(srcNpt);
1169 }
1170 if (this.portLinks.get(dstNpt) != null) {
1171 this.portLinks.get(dstNpt).remove(lt);
1172 if (this.portLinks.get(dstNpt).isEmpty())
1173 this.portLinks.remove(dstNpt);
1174 }
1175
1176 LinkInfo info = this.links.remove(lt);
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001177 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001178 lt.getDst(), lt.getDstPort(),
1179 getLinkType(lt, info),
1180 UpdateOperation.LINK_REMOVED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001181 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001182
1183 // Update Event History
1184 evHistTopoLink(lt.getSrc(),
1185 lt.getDst(),
1186 lt.getSrcPort(),
1187 lt.getDstPort(),
1188 0, 0, // Port states
1189 ILinkDiscovery.LinkType.INVALID_LINK,
1190 EvAction.LINK_DELETED, reason);
1191
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001192 // TODO Whenever link is removed, it has to checked if
1193 // the switchports must be added to quarantine.
1194
1195 if (log.isTraceEnabled()) {
1196 log.trace("Deleted link {}", lt);
1197 }
1198 }
1199 } finally {
1200 lock.writeLock().unlock();
1201 }
1202 }
1203
1204 /**
1205 * Handles an OFPortStatus message from a switch. We will add or
1206 * delete LinkTupes as well re-compute the topology if needed.
1207 * @param sw The IOFSwitch that sent the port status message
1208 * @param ps The OFPortStatus message
1209 * @return The Command to continue or stop after we process this message
1210 */
1211 protected Command handlePortStatus(long sw, OFPortStatus ps) {
1212
1213 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
1214 if (iofSwitch == null) return Command.CONTINUE;
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -07001215
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001216 // ONOS: If we do not control this switch, then we should not process its port status messages
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001217 if (!registryService.hasControl(iofSwitch.getId())) return Command.CONTINUE;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001218
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001219 if (log.isTraceEnabled()) {
1220 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
1221 "config is {} state is {}",
1222 new Object[] {iofSwitch.getStringId(),
1223 ps.getDesc().getPortNumber(),
1224 ps.getReason(),
1225 ps.getDesc().getConfig(),
1226 ps.getDesc().getState()});
1227 }
1228
1229 short port = ps.getDesc().getPortNumber();
1230 NodePortTuple npt = new NodePortTuple(sw, port);
1231 boolean linkDeleted = false;
1232 boolean linkInfoChanged = false;
1233
1234 lock.writeLock().lock();
1235 try {
1236 // if ps is a delete, or a modify where the port is down or
1237 // configured down
1238 if ((byte)OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
1239 ((byte)OFPortReason.OFPPR_MODIFY.ordinal() ==
1240 ps.getReason() && !portEnabled(ps.getDesc()))) {
1241 deleteLinksOnPort(npt, "Port Status Changed");
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001242 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, UpdateOperation.PORT_DOWN));
1243 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001244 linkDeleted = true;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001245 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001246 else if (ps.getReason() ==
1247 (byte)OFPortReason.OFPPR_MODIFY.ordinal()) {
1248 // If ps is a port modification and the port state has changed
1249 // that affects links in the topology
1250
1251 if (this.portLinks.containsKey(npt)) {
1252 for (Link lt: this.portLinks.get(npt)) {
1253 LinkInfo linkInfo = links.get(lt);
1254 assert(linkInfo != null);
1255 Integer updatedSrcPortState = null;
1256 Integer updatedDstPortState = null;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001257 if (lt.getSrc() == npt.getNodeId() &&
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001258 lt.getSrcPort() == npt.getPortId() &&
1259 (linkInfo.getSrcPortState() !=
1260 ps.getDesc().getState())) {
1261 updatedSrcPortState = ps.getDesc().getState();
1262 linkInfo.setSrcPortState(updatedSrcPortState);
1263 }
1264 if (lt.getDst() == npt.getNodeId() &&
1265 lt.getDstPort() == npt.getPortId() &&
1266 (linkInfo.getDstPortState() !=
1267 ps.getDesc().getState())) {
1268 updatedDstPortState = ps.getDesc().getState();
1269 linkInfo.setDstPortState(updatedDstPortState);
1270 }
1271 if ((updatedSrcPortState != null) ||
1272 (updatedDstPortState != null)) {
1273 // The link is already known to link discovery
1274 // manager and the status has changed, therefore
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001275 // send an LinkUpdate.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001276 UpdateOperation operation =
1277 getUpdateOperation(linkInfo.getSrcPortState(),
1278 linkInfo.getDstPortState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001279 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001280 lt.getDst(), lt.getDstPort(),
1281 getLinkType(lt, linkInfo),
1282 operation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001283 controller.publishUpdate(update);
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001284
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001285 linkInfoChanged = true;
1286 }
1287 }
1288 }
1289
1290 UpdateOperation operation =
1291 getUpdateOperation(ps.getDesc().getState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001292 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
1293 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001294 }
1295
1296 if (!linkDeleted && !linkInfoChanged){
1297 if (log.isTraceEnabled()) {
1298 log.trace("handlePortStatus: Switch {} port #{} reason {};"+
1299 " no links to update/remove",
1300 new Object[] {HexString.toHexString(sw),
1301 ps.getDesc().getPortNumber(),
1302 ps.getReason()});
1303 }
1304 }
1305 } finally {
1306 lock.writeLock().unlock();
1307 }
1308
1309 if (!linkDeleted) {
1310 // Send LLDP right away when port state is changed for faster
1311 // cluster-merge. If it is a link delete then there is not need
1312 // to send the LLDPs right away and instead we wait for the LLDPs
1313 // to be sent on the timer as it is normally done
1314 // do it outside the write-lock
1315 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
1316 processNewPort(npt.getNodeId(), npt.getPortId());
1317 }
1318 return Command.CONTINUE;
1319 }
1320
1321 /**
1322 * Process a new port.
1323 * If link discovery is disabled on the port, then do nothing.
1324 * If autoportfast feature is enabled and the port is a fast port, then
1325 * do nothing.
1326 * Otherwise, send LLDP message. Add the port to quarantine.
1327 * @param sw
1328 * @param p
1329 */
1330 private void processNewPort(long sw, short p) {
1331 if (isLinkDiscoverySuppressed(sw, p)) {
1332 // Do nothing as link discovery is suppressed.
1333 }
1334 else if (autoPortFastFeature && isFastPort(sw, p)) {
1335 // Do nothing as the port is a fast port.
1336 }
1337 else {
1338 NodePortTuple npt = new NodePortTuple(sw, p);
1339 discover(sw, p);
1340 // if it is not a fast port, add it to quarantine.
1341 if (!isFastPort(sw, p)) {
1342 addToQuarantineQueue(npt);
1343 } else {
1344 // Add to maintenance queue to ensure that BDDP packets
1345 // are sent out.
1346 addToMaintenanceQueue(npt);
1347 }
1348 }
1349 }
1350
1351 /**
1352 * We send out LLDP messages when a switch is added to discover the topology
1353 * @param sw The IOFSwitch that connected to the controller
1354 */
1355 @Override
1356 public void addedSwitch(IOFSwitch sw) {
1357
1358 if (sw.getEnabledPorts() != null) {
1359 for (Short p : sw.getEnabledPortNumbers()) {
1360 processNewPort(sw.getId(), p);
1361 }
1362 }
1363 // Update event history
1364 evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001365 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), null,
1366 UpdateOperation.SWITCH_UPDATED));
1367 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001368 }
1369
1370 /**
1371 * When a switch disconnects we remove any links from our map and notify.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001372 */
1373 @Override
1374 public void removedSwitch(IOFSwitch iofSwitch) {
1375 // Update event history
1376 long sw = iofSwitch.getId();
1377 evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None");
1378 List<Link> eraseList = new ArrayList<Link>();
1379 lock.writeLock().lock();
1380 try {
1381 if (switchLinks.containsKey(sw)) {
1382 if (log.isTraceEnabled()) {
1383 log.trace("Handle switchRemoved. Switch {}; removing links {}",
1384 HexString.toHexString(sw), switchLinks.get(sw));
1385 }
1386 // add all tuples with an endpoint on this switch to erase list
1387 eraseList.addAll(switchLinks.get(sw));
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -07001388 deleteLinks(eraseList, "Switch Removed");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001389
1390 // Send a switch removed update
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001391 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED));
1392 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001393 }
1394 } finally {
1395 lock.writeLock().unlock();
1396 }
1397 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001398
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001399 /**
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001400 * We don't react the port changed notifications here. we listen for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001401 * OFPortStatus messages directly. Might consider using this notifier
1402 * instead
1403 */
1404 @Override
1405 public void switchPortChanged(Long switchId) {
1406 // no-op
1407 }
1408
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001409 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001410 * Delete links incident on a given switch port.
1411 * @param npt
1412 * @param reason
1413 */
1414 protected void deleteLinksOnPort(NodePortTuple npt, String reason) {
1415 List<Link> eraseList = new ArrayList<Link>();
1416 if (this.portLinks.containsKey(npt)) {
1417 if (log.isTraceEnabled()) {
1418 log.trace("handlePortStatus: Switch {} port #{} " +
1419 "removing links {}",
1420 new Object[] {HexString.toHexString(npt.getNodeId()),
1421 npt.getPortId(),
1422 this.portLinks.get(npt)});
1423 }
1424 eraseList.addAll(this.portLinks.get(npt));
1425 deleteLinks(eraseList, reason);
1426 }
1427 }
1428
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001429 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001430 * Iterates through the list of links and deletes if the
1431 * last discovery message reception time exceeds timeout values.
1432 */
1433 protected void timeoutLinks() {
1434 List<Link> eraseList = new ArrayList<Link>();
1435 Long curTime = System.currentTimeMillis();
1436 boolean linkChanged = false;
1437
1438 // reentrant required here because deleteLink also write locks
1439 lock.writeLock().lock();
1440 try {
1441 Iterator<Entry<Link, LinkInfo>> it =
1442 this.links.entrySet().iterator();
1443 while (it.hasNext()) {
1444 Entry<Link, LinkInfo> entry = it.next();
1445 Link lt = entry.getKey();
1446 LinkInfo info = entry.getValue();
1447
1448 // Timeout the unicast and multicast LLDP valid times
1449 // independently.
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001450 if ((info.getUnicastValidTime() != null) &&
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001451 (info.getUnicastValidTime() + (this.LINK_TIMEOUT * 1000) < curTime)){
1452 info.setUnicastValidTime(null);
1453
1454 if (info.getMulticastValidTime() != null)
1455 addLinkToBroadcastDomain(lt);
1456 // Note that even if mTime becomes null later on,
1457 // the link would be deleted, which would trigger updateClusters().
1458 linkChanged = true;
1459 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001460 if ((info.getMulticastValidTime()!= null) &&
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001461 (info.getMulticastValidTime()+ (this.LINK_TIMEOUT * 1000) < curTime)) {
1462 info.setMulticastValidTime(null);
1463 // if uTime is not null, then link will remain as openflow
1464 // link. If uTime is null, it will be deleted. So, we
1465 // don't care about linkChanged flag here.
1466 removeLinkFromBroadcastDomain(lt);
1467 linkChanged = true;
1468 }
1469 // Add to the erase list only if the unicast
1470 // time is null.
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001471 if (info.getUnicastValidTime() == null &&
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001472 info.getMulticastValidTime() == null){
1473 eraseList.add(entry.getKey());
1474 } else if (linkChanged) {
1475 UpdateOperation operation;
1476 operation = getUpdateOperation(info.getSrcPortState(),
1477 info.getDstPortState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001478 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001479 lt.getDst(), lt.getDstPort(),
1480 getLinkType(lt, info),
1481 operation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001482 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001483 }
1484 }
1485
1486 // if any link was deleted or any link was changed.
1487 if ((eraseList.size() > 0) || linkChanged) {
1488 deleteLinks(eraseList, "LLDP timeout");
1489 }
1490 } finally {
1491 lock.writeLock().unlock();
1492 }
1493 }
1494
1495 private boolean portEnabled(OFPhysicalPort port) {
1496 if (port == null)
1497 return false;
1498 if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0)
1499 return false;
1500 if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0)
1501 return false;
1502 // Port STP state doesn't work with multiple VLANs, so ignore it for now
1503 // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
1504 // return false;
1505 return true;
1506 }
1507
1508 public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
1509 return portBroadcastDomainLinks;
1510 }
1511
1512 @Override
1513 public Map<Link, LinkInfo> getLinks() {
1514 lock.readLock().lock();
1515 Map<Link, LinkInfo> result;
1516 try {
1517 result = new HashMap<Link, LinkInfo>(links);
1518 } finally {
1519 lock.readLock().unlock();
1520 }
1521 return result;
1522 }
1523
1524 protected void addLinkToBroadcastDomain(Link lt) {
1525
1526 NodePortTuple srcNpt, dstNpt;
1527 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1528 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1529
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001530 if (!portBroadcastDomainLinks.containsKey(srcNpt))
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001531 portBroadcastDomainLinks.put(srcNpt, new HashSet<Link>());
1532 portBroadcastDomainLinks.get(srcNpt).add(lt);
1533
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001534 if (!portBroadcastDomainLinks.containsKey(dstNpt))
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001535 portBroadcastDomainLinks.put(dstNpt, new HashSet<Link>());
1536 portBroadcastDomainLinks.get(dstNpt).add(lt);
1537 }
1538
1539 protected void removeLinkFromBroadcastDomain(Link lt) {
1540
1541 NodePortTuple srcNpt, dstNpt;
1542 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1543 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1544
1545 if (portBroadcastDomainLinks.containsKey(srcNpt)) {
1546 portBroadcastDomainLinks.get(srcNpt).remove(lt);
1547 if (portBroadcastDomainLinks.get(srcNpt).isEmpty())
1548 portBroadcastDomainLinks.remove(srcNpt);
1549 }
1550
1551 if (portBroadcastDomainLinks.containsKey(dstNpt)) {
1552 portBroadcastDomainLinks.get(dstNpt).remove(lt);
1553 if (portBroadcastDomainLinks.get(dstNpt).isEmpty())
1554 portBroadcastDomainLinks.remove(dstNpt);
1555 }
1556 }
1557
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001558 @Override
1559 public void addListener(ILinkDiscoveryListener listener) {
1560 linkDiscoveryAware.add(listener);
1561 }
1562
1563 /**
1564 * Register a link discovery aware component
1565 * @param linkDiscoveryAwareComponent
1566 */
1567 public void addLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
1568 // TODO make this a copy on write set or lock it somehow
1569 this.linkDiscoveryAware.add(linkDiscoveryAwareComponent);
1570 }
1571
1572 /**
1573 * Deregister a link discovery aware component
1574 * @param linkDiscoveryAwareComponent
1575 */
1576 public void removeLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
1577 // TODO make this a copy on write set or lock it somehow
1578 this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent);
1579 }
1580
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001581 @Override
1582 public boolean isCallbackOrderingPrereq(OFType type, String name) {
1583 return false;
1584 }
1585
1586 @Override
1587 public boolean isCallbackOrderingPostreq(OFType type, String name) {
1588 return false;
1589 }
1590
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001591 // IFloodlightModule classes
1592
1593 @Override
1594 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001595 Collection<Class<? extends IFloodlightService>> l =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001596 new ArrayList<Class<? extends IFloodlightService>>();
1597 l.add(ILinkDiscoveryService.class);
1598 //l.add(ITopologyService.class);
1599 return l;
1600 }
1601
1602 @Override
1603 public Map<Class<? extends IFloodlightService>, IFloodlightService>
1604 getServiceImpls() {
1605 Map<Class<? extends IFloodlightService>,
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001606 IFloodlightService> m =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001607 new HashMap<Class<? extends IFloodlightService>,
1608 IFloodlightService>();
1609 // We are the class that implements the service
1610 m.put(ILinkDiscoveryService.class, this);
1611 return m;
1612 }
1613
1614 @Override
1615 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001616 Collection<Class<? extends IFloodlightService>> l =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001617 new ArrayList<Class<? extends IFloodlightService>>();
1618 l.add(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001619 l.add(IThreadPoolService.class);
1620 l.add(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001621 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001622 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001623 return l;
1624 }
1625
1626 @Override
1627 public void init(FloodlightModuleContext context)
1628 throws FloodlightModuleException {
1629 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001630 threadPool = context.getServiceImpl(IThreadPoolService.class);
1631 restApi = context.getServiceImpl(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001632 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001633 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001634
1635 // Set the autoportfast feature to false.
1636 this.autoPortFastFeature = false;
1637
1638 // We create this here because there is no ordering guarantee
1639 this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
1640 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001641 this.links = new HashMap<Link, LinkInfo>();
1642 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
1643 this.suppressLinkDiscovery =
1644 Collections.synchronizedSet(new HashSet<NodePortTuple>());
1645 this.portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
1646 this.switchLinks = new HashMap<Long, Set<Link>>();
1647 this.quarantineQueue = new LinkedBlockingQueue<NodePortTuple>();
1648 this.maintenanceQueue = new LinkedBlockingQueue<NodePortTuple>();
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001649 // Added by ONOS
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -07001650 this.remoteSwitches = new HashMap<Long, IOnosRemoteSwitch>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001651
1652 this.evHistTopologySwitch =
1653 new EventHistory<EventHistoryTopologySwitch>("Topology: Switch");
1654 this.evHistTopologyLink =
1655 new EventHistory<EventHistoryTopologyLink>("Topology: Link");
1656 this.evHistTopologyCluster =
1657 new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster");
1658 }
1659
1660 @Override
1661 @LogMessageDocs({
1662 @LogMessageDoc(level="ERROR",
1663 message="No storage source found.",
1664 explanation="Storage source was not initialized; cannot initialize " +
1665 "link discovery.",
1666 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1667 @LogMessageDoc(level="ERROR",
1668 message="Error in installing listener for " +
1669 "switch config table {table}",
1670 explanation="Failed to install storage notification for the " +
1671 "switch config table",
1672 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1673 @LogMessageDoc(level="ERROR",
1674 message="No storage source found.",
1675 explanation="Storage source was not initialized; cannot initialize " +
1676 "link discovery.",
1677 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1678 @LogMessageDoc(level="ERROR",
1679 message="Exception in LLDP send timer.",
1680 explanation="An unknown error occured while sending LLDP " +
1681 "messages to switches.",
1682 recommendation=LogMessageDoc.CHECK_SWITCH)
1683 })
1684 public void startUp(FloodlightModuleContext context) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001685 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001686 controller =
1687 context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001688
1689 // To be started by the first switch connection
1690 discoveryTask = new SingletonTask(ses, new Runnable() {
1691 @Override
1692 public void run() {
1693 try {
1694 discoverLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001695 } catch (Exception e) {
1696 log.error("Exception in LLDP send timer.", e);
1697 } finally {
1698 if (!shuttingDown) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001699 // Always reschedule link discovery if we're not
Jonathan Hartec4f14e2013-12-12 10:46:38 -08001700 // shutting down (no chance of SLAVE role now)
1701 log.trace("Rescheduling discovery task");
1702 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
1703 TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001704 }
1705 }
1706 }
1707 });
1708
Jonathan Hartec4f14e2013-12-12 10:46:38 -08001709 // Always reschedule link discovery as we are never in SLAVE role now
1710 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001711
1712 // Setup the BDDP task. It is invoked whenever switch port tuples
1713 // are added to the quarantine list.
1714 bddpTask = new SingletonTask(ses, new QuarantineWorker());
1715 bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
1716
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001717
1718 // Register for the OpenFlow messages we want to receive
1719 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1720 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1721 // Register for switch updates
1722 floodlightProvider.addOFSwitchListener(this);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001723 if (restApi != null)
1724 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
1725 setControllerTLV();
1726 }
1727
1728 // ****************************************************
1729 // Topology Manager's Event History members and methods
1730 // ****************************************************
1731
1732 // Topology Manager event history
1733 public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch;
1734 public EventHistory<EventHistoryTopologyLink> evHistTopologyLink;
1735 public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster;
1736 public EventHistoryTopologySwitch evTopoSwitch;
1737 public EventHistoryTopologyLink evTopoLink;
1738 public EventHistoryTopologyCluster evTopoCluster;
1739
1740 // Switch Added/Deleted
1741 private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
1742 if (evTopoSwitch == null) {
1743 evTopoSwitch = new EventHistoryTopologySwitch();
1744 }
1745 evTopoSwitch.dpid = sw.getId();
1746 if ((sw.getChannel() != null) &&
1747 (SocketAddress.class.isInstance(
1748 sw.getChannel().getRemoteAddress()))) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001749 evTopoSwitch.ipv4Addr =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001750 IPv4.toIPv4Address(((InetSocketAddress)(sw.getChannel().
1751 getRemoteAddress())).getAddress().getAddress());
1752 evTopoSwitch.l4Port =
1753 ((InetSocketAddress)(sw.getChannel().
1754 getRemoteAddress())).getPort();
1755 } else {
1756 evTopoSwitch.ipv4Addr = 0;
1757 evTopoSwitch.l4Port = 0;
1758 }
1759 evTopoSwitch.reason = reason;
1760 evTopoSwitch = evHistTopologySwitch.put(evTopoSwitch, actn);
1761 }
1762
1763 private void evHistTopoLink(long srcDpid, long dstDpid, short srcPort,
1764 short dstPort, int srcPortState, int dstPortState,
1765 ILinkDiscovery.LinkType linkType,
1766 EvAction actn, String reason) {
1767 if (evTopoLink == null) {
1768 evTopoLink = new EventHistoryTopologyLink();
1769 }
1770 evTopoLink.srcSwDpid = srcDpid;
1771 evTopoLink.dstSwDpid = dstDpid;
1772 evTopoLink.srcSwport = srcPort & 0xffff;
1773 evTopoLink.dstSwport = dstPort & 0xffff;
1774 evTopoLink.srcPortState = srcPortState;
1775 evTopoLink.dstPortState = dstPortState;
1776 evTopoLink.reason = reason;
1777 switch (linkType) {
1778 case DIRECT_LINK:
1779 evTopoLink.linkType = "DIRECT_LINK";
1780 break;
1781 case MULTIHOP_LINK:
1782 evTopoLink.linkType = "MULTIHOP_LINK";
1783 break;
1784 case TUNNEL:
1785 evTopoLink.linkType = "TUNNEL";
1786 break;
1787 case INVALID_LINK:
1788 default:
1789 evTopoLink.linkType = "Unknown";
1790 break;
1791 }
1792 evTopoLink = evHistTopologyLink.put(evTopoLink, actn);
1793 }
1794
1795 public void evHistTopoCluster(long dpid, long clusterIdOld,
1796 long clusterIdNew, EvAction action, String reason) {
1797 if (evTopoCluster == null) {
1798 evTopoCluster = new EventHistoryTopologyCluster();
1799 }
1800 evTopoCluster.dpid = dpid;
1801 evTopoCluster.clusterIdOld = clusterIdOld;
1802 evTopoCluster.clusterIdNew = clusterIdNew;
1803 evTopoCluster.reason = reason;
1804 evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action);
1805 }
1806
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001807 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001808 public boolean isAutoPortFastFeature() {
1809 return autoPortFastFeature;
1810 }
1811
Yuta HIGUCHIe8813402014-01-08 13:36:50 -08001812 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001813 public void setAutoPortFastFeature(boolean autoPortFastFeature) {
1814 this.autoPortFastFeature = autoPortFastFeature;
1815 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001816}