blob: 1a890cb0c5b13483e9ce8faabfb1c0ce67f83621 [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;
45import net.floodlightcontroller.core.IInfoProvider;
46import net.floodlightcontroller.core.IOFMessageListener;
47import net.floodlightcontroller.core.IOFSwitch;
48import net.floodlightcontroller.core.IOFSwitchListener;
49import net.floodlightcontroller.core.annotations.LogMessageCategory;
50import net.floodlightcontroller.core.annotations.LogMessageDoc;
51import net.floodlightcontroller.core.annotations.LogMessageDocs;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070052import net.floodlightcontroller.core.internal.OFSwitchImpl;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080053import net.floodlightcontroller.core.module.FloodlightModuleContext;
54import net.floodlightcontroller.core.module.FloodlightModuleException;
55import net.floodlightcontroller.core.module.IFloodlightModule;
56import net.floodlightcontroller.core.module.IFloodlightService;
57import net.floodlightcontroller.core.util.SingletonTask;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080058import net.floodlightcontroller.packet.BSN;
59import net.floodlightcontroller.packet.Ethernet;
60import net.floodlightcontroller.packet.IPv4;
61import net.floodlightcontroller.packet.LLDP;
62import net.floodlightcontroller.packet.LLDPTLV;
63import net.floodlightcontroller.restserver.IRestApiService;
64import net.floodlightcontroller.routing.Link;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080065import net.floodlightcontroller.threadpool.IThreadPoolService;
66import net.floodlightcontroller.topology.NodePortTuple;
67import net.floodlightcontroller.util.EventHistory;
68import net.floodlightcontroller.util.EventHistory.EvAction;
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -070069import net.onrc.onos.ofcontroller.core.IOnosRemoteSwitch;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070070import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery;
Jonathan Hart2fa28062013-11-25 20:16:28 -080071import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
72import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.UpdateOperation;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070073import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryListener;
74import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
75import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070076import net.onrc.onos.ofcontroller.linkdiscovery.web.LinkDiscoveryWebRoutable;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070077import net.onrc.onos.registry.controller.IControllerRegistryService;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070078
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080079import org.openflow.protocol.OFMessage;
80import org.openflow.protocol.OFPacketIn;
81import org.openflow.protocol.OFPacketOut;
82import org.openflow.protocol.OFPhysicalPort;
83import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
84import org.openflow.protocol.OFPhysicalPort.OFPortState;
85import org.openflow.protocol.OFPort;
86import org.openflow.protocol.OFPortStatus;
87import org.openflow.protocol.OFPortStatus.OFPortReason;
88import org.openflow.protocol.OFType;
89import org.openflow.protocol.action.OFAction;
90import org.openflow.protocol.action.OFActionOutput;
91import org.openflow.util.HexString;
92import org.slf4j.Logger;
93import org.slf4j.LoggerFactory;
94
95/**
96 * This class sends out LLDP messages containing the sending switch's datapath
97 * id as well as the outgoing port number. Received LLrescDP messages that
98 * match a known switch cause a new LinkTuple to be created according to the
99 * invariant rules listed below. This new LinkTuple is also passed to routing
100 * if it exists to trigger updates.
101 *
102 * This class also handles removing links that are associated to switch ports
103 * that go down, and switches that are disconnected.
104 *
105 * Invariants:
106 * -portLinks and switchLinks will not contain empty Sets outside of
107 * critical sections
108 * -portLinks contains LinkTuples where one of the src or dst
109 * SwitchPortTuple matches the map key
110 * -switchLinks contains LinkTuples where one of the src or dst
111 * SwitchPortTuple's id matches the switch id
112 * -Each LinkTuple will be indexed into switchLinks for both
113 * src.id and dst.id, and portLinks for each src and dst
114 * -The updates queue is only added to from within a held write lock
115 */
116@LogMessageCategory("Network Topology")
117public class LinkDiscoveryManager
118implements IOFMessageListener, IOFSwitchListener,
Jonathan Hart2fa28062013-11-25 20:16:28 -0800119ILinkDiscoveryService,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800120IFloodlightModule, IInfoProvider, IHAListener {
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700121 protected IFloodlightProviderService controller;
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700122 protected final static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800123
Jonathan Hart2fa28062013-11-25 20:16:28 -0800124 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800125 // Names of table/fields for links in the storage API
126 private static final String LINK_TABLE_NAME = "controller_link";
127 private static final String LINK_ID = "id";
128 private static final String LINK_SRC_SWITCH = "src_switch_id";
129 private static final String LINK_SRC_PORT = "src_port";
130 private static final String LINK_SRC_PORT_STATE = "src_port_state";
131 private static final String LINK_DST_SWITCH = "dst_switch_id";
132 private static final String LINK_DST_PORT = "dst_port";
133 private static final String LINK_DST_PORT_STATE = "dst_port_state";
134 private static final String LINK_VALID_TIME = "valid_time";
135 private static final String LINK_TYPE = "link_type";
136 private static final String SWITCH_CONFIG_TABLE_NAME = "controller_switchconfig";
137 private static final String SWITCH_CONFIG_CORE_SWITCH = "core_switch";
Jonathan Hart2fa28062013-11-25 20:16:28 -0800138 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800139
140 protected IFloodlightProviderService floodlightProvider;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800141 protected IThreadPoolService threadPool;
142 protected IRestApiService restApi;
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700143 // Registry Service for ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700144 protected IControllerRegistryService registryService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800145
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700146
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800147 // LLDP and BDDP fields
148 private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
149 HexString.fromHexString("01:80:c2:00:00:0e");
150 private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
151 private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
152
153 // BigSwitch OUI is 5C:16:C7, so 5D:16:C7 is the multicast version
Masayoshi Kobayashi71137362013-07-12 15:54:52 -0700154 // private static final String LLDP_BSN_DST_MAC_STRING = "5d:16:c7:00:00:01";
155 private static final String LLDP_BSN_DST_MAC_STRING = "ff:ff:ff:ff:ff:ff";
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800156
157
158 // Direction TLVs are used to indicate if the LLDPs were sent
159 // periodically or in response to a recieved LLDP
160 private static final byte TLV_DIRECTION_TYPE = 0x73;
161 private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte
162 private static final byte TLV_DIRECTION_VALUE_FORWARD[] = {0x01};
163 private static final byte TLV_DIRECTION_VALUE_REVERSE[] = {0x02};
164 private static final LLDPTLV forwardTLV
165 = new LLDPTLV().
166 setType((byte)TLV_DIRECTION_TYPE).
167 setLength((short)TLV_DIRECTION_LENGTH).
168 setValue(TLV_DIRECTION_VALUE_FORWARD);
169
170 private static final LLDPTLV reverseTLV
171 = new LLDPTLV().
172 setType((byte)TLV_DIRECTION_TYPE).
173 setLength((short)TLV_DIRECTION_LENGTH).
174 setValue(TLV_DIRECTION_VALUE_REVERSE);
175
176 // Link discovery task details.
177 protected SingletonTask discoveryTask;
178 protected final int DISCOVERY_TASK_INTERVAL = 1;
Umesh Krishnaswamy77601202013-04-03 21:46:01 -0700179 protected final int LINK_TIMEOUT = 35; // original 35 secs, aggressive 5 secs
180 protected final int LLDP_TO_ALL_INTERVAL = 15 ; //original 15 seconds, aggressive 2 secs.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800181 protected long lldpClock = 0;
182 // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
183 // If we want to identify link failures faster, we could decrease this
184 // value to a small number, say 1 or 2 sec.
Umesh Krishnaswamy77601202013-04-03 21:46:01 -0700185 protected final int LLDP_TO_KNOWN_INTERVAL= 20; // LLDP frequency for known links
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800186
187 protected LLDPTLV controllerTLV;
188 protected ReentrantReadWriteLock lock;
189 int lldpTimeCount = 0;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700190
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800191 /**
192 * Flag to indicate if automatic port fast is enabled or not.
193 * Default is set to false -- Initialized in the init method as well.
194 */
195 boolean autoPortFastFeature = false;
196
197 /**
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800198 * Map of remote switches that are not connected to this controller. This
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700199 * is used to learn remote switches in a distributed controller ONOS.
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800200 */
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -0700201 protected Map<Long, IOnosRemoteSwitch> remoteSwitches;
HIGUCHI Yuta3d96f652013-06-17 12:07:48 -0700202
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800203 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800204 * Map from link to the most recent time it was verified functioning
205 */
206 protected Map<Link, LinkInfo> links;
207
208 /**
209 * Map from switch id to a set of all links with it as an endpoint
210 */
211 protected Map<Long, Set<Link>> switchLinks;
212
213 /**
214 * Map from a id:port to the set of links containing it as an endpoint
215 */
216 protected Map<NodePortTuple, Set<Link>> portLinks;
217
218 /**
219 * Set of link tuples over which multicast LLDPs are received
220 * and unicast LLDPs are not received.
221 */
222 protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks;
223
224 protected volatile boolean shuttingDown = false;
225
226 /* topology aware components are called in the order they were added to the
227 * the array */
228 protected ArrayList<ILinkDiscoveryListener> linkDiscoveryAware;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700229
230 protected class LinkUpdate extends LDUpdate {
231
232 public LinkUpdate(LDUpdate old) {
233 super(old);
234 }
235 @LogMessageDoc(level="ERROR",
236 message="Error in link discovery updates loop",
237 explanation="An unknown error occured while dispatching " +
238 "link update notifications",
239 recommendation=LogMessageDoc.GENERIC_ACTION)
240 @Override
241 public void dispatch() {
242 if (linkDiscoveryAware != null) {
243 if (log.isTraceEnabled()) {
244 log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
245 new Object[]{this.getOperation(),
246 HexString.toHexString(this.getSrc()), this.getSrcPort(),
247 HexString.toHexString(this.getDst()), this.getDstPort(),
248 linkDiscoveryAware});
249 }
250 try {
251 for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained
252 lda.linkDiscoveryUpdate(this);
253 }
254 }
255 catch (Exception e) {
256 log.error("Error in link discovery updates loop", e);
257 }
258 }
259
260 }
261
262 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800263
264 /**
265 * List of ports through which LLDP/BDDPs are not sent.
266 */
267 protected Set<NodePortTuple> suppressLinkDiscovery;
268
269 /** A list of ports that are quarantined for discovering links through
270 * them. Data traffic from these ports are not allowed until the ports
271 * are released from quarantine.
272 */
273 protected LinkedBlockingQueue<NodePortTuple> quarantineQueue;
274 protected LinkedBlockingQueue<NodePortTuple> maintenanceQueue;
275 /**
276 * Quarantine task
277 */
278 protected SingletonTask bddpTask;
279 protected final int BDDP_TASK_INTERVAL = 100; // 100 ms.
280 protected final int BDDP_TASK_SIZE = 5; // # of ports per iteration
281
282 /**
283 * Map of broadcast domain ports and the last time a BDDP was either
284 * sent or received on that port.
285 */
286 protected Map<NodePortTuple, Long> broadcastDomainPortTimeMap;
287
288 /**
289 * Get the LLDP sending period in seconds.
290 * @return LLDP sending period in seconds.
291 */
292 public int getLldpFrequency() {
293 return LLDP_TO_KNOWN_INTERVAL;
294 }
295
296 /**
297 * Get the LLDP timeout value in seconds
298 * @return LLDP timeout value in seconds
299 */
300 public int getLldpTimeout() {
301 return LINK_TIMEOUT;
302 }
303
304 public Map<NodePortTuple, Set<Link>> getPortLinks() {
305 return portLinks;
306 }
307
308 public Set<NodePortTuple> getSuppressLLDPsInfo() {
309 return suppressLinkDiscovery;
310 }
311
312 /**
313 * Add a switch port to the suppressed LLDP list.
314 * Remove any known links on the switch port.
315 */
316 public void AddToSuppressLLDPs(long sw, short port)
317 {
318 NodePortTuple npt = new NodePortTuple(sw, port);
319 this.suppressLinkDiscovery.add(npt);
320 deleteLinksOnPort(npt, "LLDP suppressed.");
321 }
322
323 /**
324 * Remove a switch port from the suppressed LLDP list.
325 * Discover links on that switchport.
326 */
327 public void RemoveFromSuppressLLDPs(long sw, short port)
328 {
329 NodePortTuple npt = new NodePortTuple(sw, port);
330 this.suppressLinkDiscovery.remove(npt);
331 discover(npt);
332 }
333
334 public boolean isShuttingDown() {
335 return shuttingDown;
336 }
337
338 public boolean isFastPort(long sw, short port) {
339 return false;
340 }
341
342 public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
343 if (info.getUnicastValidTime() != null) {
344 return ILinkDiscovery.LinkType.DIRECT_LINK;
345 } else if (info.getMulticastValidTime() != null) {
346 return ILinkDiscovery.LinkType.MULTIHOP_LINK;
347 }
348 return ILinkDiscovery.LinkType.INVALID_LINK;
349 }
350
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700351
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800352 private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
353 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
354 }
355
356 protected void discoverLinks() {
357
358 // timeout known links.
359 timeoutLinks();
360
361 //increment LLDP clock
362 lldpClock = (lldpClock + 1)% LLDP_TO_ALL_INTERVAL;
363
364 if (lldpClock == 0) {
365 log.debug("Sending LLDP out on all ports.");
366 discoverOnAllPorts();
367 }
368 }
369
370
371 /**
372 * Quarantine Ports.
373 */
374 protected class QuarantineWorker implements Runnable {
375 @Override
376 public void run() {
377 try {
378 processBDDPLists();
379 }
380 catch (Exception e) {
381 log.error("Error in quarantine worker thread", e);
382 } finally {
383 bddpTask.reschedule(BDDP_TASK_INTERVAL,
384 TimeUnit.MILLISECONDS);
385 }
386 }
387 }
388
389 /**
390 * Add a switch port to the quarantine queue. Schedule the
391 * quarantine task if the quarantine queue was empty before adding
392 * this switch port.
393 * @param npt
394 */
395 protected void addToQuarantineQueue(NodePortTuple npt) {
396 if (quarantineQueue.contains(npt) == false)
397 quarantineQueue.add(npt);
398 }
399
400 /**
401 * Remove a switch port from the quarantine queue.
402 */
403 protected void removeFromQuarantineQueue(NodePortTuple npt) {
404 // Remove all occurrences of the node port tuple from the list.
405 while (quarantineQueue.remove(npt));
406 }
407
408 /**
409 * Add a switch port to maintenance queue.
410 * @param npt
411 */
412 protected void addToMaintenanceQueue(NodePortTuple npt) {
413 // TODO We are not checking if the switch port tuple is already
414 // in the maintenance list or not. This will be an issue for
415 // really large number of switch ports in the network.
416 if (maintenanceQueue.contains(npt) == false)
417 maintenanceQueue.add(npt);
418 }
419
420 /**
421 * Remove a switch port from maintenance queue.
422 * @param npt
423 */
424 protected void removeFromMaintenanceQueue(NodePortTuple npt) {
425 // Remove all occurrences of the node port tuple from the queue.
426 while (maintenanceQueue.remove(npt));
427 }
428
429 /**
430 * This method processes the quarantine list in bursts. The task is
431 * at most once per BDDP_TASK_INTERVAL.
432 * One each call, BDDP_TASK_SIZE number of switch ports are processed.
433 * Once the BDDP packets are sent out through the switch ports, the ports
434 * are removed from the quarantine list.
435 */
436
437 protected void processBDDPLists() {
438 int count = 0;
439 Set<NodePortTuple> nptList = new HashSet<NodePortTuple>();
440
441 while(count < BDDP_TASK_SIZE && quarantineQueue.peek() !=null) {
442 NodePortTuple npt;
443 npt = quarantineQueue.remove();
444 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
445 nptList.add(npt);
446 count++;
447 }
448
449 count = 0;
450 while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) {
451 NodePortTuple npt;
452 npt = maintenanceQueue.remove();
453 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
454 count++;
455 }
456
457 for(NodePortTuple npt:nptList) {
458 generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId());
459 }
460 }
461
462 public Set<Short> getQuarantinedPorts(long sw) {
463 Set<Short> qPorts = new HashSet<Short>();
464
465 Iterator<NodePortTuple> iter = quarantineQueue.iterator();
466 while (iter.hasNext()) {
467 NodePortTuple npt = iter.next();
468 if (npt.getNodeId() == sw) {
469 qPorts.add(npt.getPortId());
470 }
471 }
472 return qPorts;
473 }
474
475 private void generateSwitchPortStatusUpdate(long sw, short port) {
476 UpdateOperation operation;
477
478 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
479 if (iofSwitch == null) return;
480
481 OFPhysicalPort ofp = iofSwitch.getPort(port);
482 if (ofp == null) return;
483
484 int srcPortState = ofp.getState();
485 boolean portUp = ((srcPortState &
486 OFPortState.OFPPS_STP_MASK.getValue()) !=
487 OFPortState.OFPPS_STP_BLOCK.getValue());
488
489 if (portUp) operation = UpdateOperation.PORT_UP;
490 else operation = UpdateOperation.PORT_DOWN;
491
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700492 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
493
494
495 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800496 }
497
498 /**
499 * Send LLDP on known ports
500 */
501 protected void discoverOnKnownLinkPorts() {
502 // Copy the port set.
503 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
504 nptSet.addAll(portLinks.keySet());
505
506 // Send LLDP from each of them.
507 for(NodePortTuple npt: nptSet) {
508 discover(npt);
509 }
510 }
511
512 protected void discover(NodePortTuple npt) {
513 discover(npt.getNodeId(), npt.getPortId());
514 }
515
516 protected void discover(long sw, short port) {
517 sendDiscoveryMessage(sw, port, true, false);
518 }
519
520 /**
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700521 * Learn remote switches when running as a distributed controller ONOS
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800522 */
523 protected IOFSwitch addRemoteSwitch(long sw, short port) {
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -0700524 IOnosRemoteSwitch remotesw = null;
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800525
526 // add a switch if we have not seen it before
Umesh Krishnaswamy4c8e1082013-01-24 23:15:37 -0800527 remotesw = remoteSwitches.get(sw);
Jonathan Harte7231052013-01-25 00:01:14 -0800528
Pankaj Berdec125e622013-01-25 06:39:39 -0800529 if (remotesw == null) {
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800530 remotesw = new OFSwitchImpl();
531 remotesw.setupRemoteSwitch(sw);
532 remoteSwitches.put(remotesw.getId(), remotesw);
533 log.debug("addRemoteSwitch(): added fake remote sw {}", remotesw);
534 }
535
536 // add the port if we have not seen it before
Umesh Krishnaswamy68c118c2013-01-25 11:07:09 -0800537 if (remotesw.getPort(port) == null) {
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800538 OFPhysicalPort remoteport = new OFPhysicalPort();
539 remoteport.setPortNumber(port);
540 remoteport.setName("fake_" + port);
541 remoteport.setConfig(0);
542 remoteport.setState(0);
543 remotesw.setPort(remoteport);
Umesh Krishnaswamy82dcd982013-02-01 15:36:15 -0800544 log.debug("addRemoteSwitch(): added fake remote port {} to sw {}", remoteport, remotesw.getId());
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800545 }
546
547 return remotesw;
548 }
549
550 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800551 * Send link discovery message out of a given switch port.
552 * The discovery message may be a standard LLDP or a modified
553 * LLDP, where the dst mac address is set to :ff.
554 *
555 * TODO: The modified LLDP will updated in the future and may
556 * use a different eth-type.
557 * @param sw
558 * @param port
559 * @param isStandard indicates standard or modified LLDP
560 * @param isReverse indicates whether the LLDP was sent as a response
561 */
562 @LogMessageDoc(level="ERROR",
563 message="Failure sending LLDP out port {port} on switch {switch}",
564 explanation="An I/O error occured while sending LLDP message " +
565 "to the switch.",
566 recommendation=LogMessageDoc.CHECK_SWITCH)
567 protected void sendDiscoveryMessage(long sw, short port,
568 boolean isStandard,
569 boolean isReverse) {
570
571 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
572 if (iofSwitch == null) {
573 return;
574 }
575
576 if (port == OFPort.OFPP_LOCAL.getValue())
577 return;
578
579 OFPhysicalPort ofpPort = iofSwitch.getPort(port);
580
581 if (ofpPort == null) {
582 if (log.isTraceEnabled()) {
583 log.trace("Null physical port. sw={}, port={}", sw, port);
584 }
585 return;
586 }
587
588 if (isLinkDiscoverySuppressed(sw, port)) {
589 /* Dont send LLDPs out of this port as suppressLLDPs set
590 *
591 */
592 return;
593 }
594
595 // For fast ports, do not send forward LLDPs or BDDPs.
596 if (!isReverse && autoPortFastFeature && isFastPort(sw, port))
597 return;
598
599 if (log.isTraceEnabled()) {
600 log.trace("Sending LLDP packet out of swich: {}, port: {}",
601 sw, port);
602 }
603
604 // using "nearest customer bridge" MAC address for broadest possible propagation
605 // through provider and TPMR bridges (see IEEE 802.1AB-2009 and 802.1Q-2011),
606 // in particular the Linux bridge which behaves mostly like a provider bridge
607 byte[] chassisId = new byte[] {4, 0, 0, 0, 0, 0, 0}; // filled in later
608 byte[] portId = new byte[] {2, 0, 0}; // filled in later
609 byte[] ttlValue = new byte[] {0, 0x78};
610 // OpenFlow OUI - 00-26-E1
611 byte[] dpidTLVValue = new byte[] {0x0, 0x26, (byte) 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
612 LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127).setLength((short) dpidTLVValue.length).setValue(dpidTLVValue);
613
614 byte[] dpidArray = new byte[8];
615 ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
616 ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
617
618 Long dpid = sw;
619 dpidBB.putLong(dpid);
620 // set the ethernet source mac to last 6 bytes of dpid
621 System.arraycopy(dpidArray, 2, ofpPort.getHardwareAddress(), 0, 6);
622 // set the chassis id's value to last 6 bytes of dpid
623 System.arraycopy(dpidArray, 2, chassisId, 1, 6);
624 // set the optional tlv to the full dpid
625 System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
626
627
628 // set the portId to the outgoing port
629 portBB.putShort(port);
630 if (log.isTraceEnabled()) {
631 log.trace("Sending LLDP out of interface: {}/{}",
632 HexString.toHexString(sw), port);
633 }
634
635 LLDP lldp = new LLDP();
636 lldp.setChassisId(new LLDPTLV().setType((byte) 1).setLength((short) chassisId.length).setValue(chassisId));
637 lldp.setPortId(new LLDPTLV().setType((byte) 2).setLength((short) portId.length).setValue(portId));
638 lldp.setTtl(new LLDPTLV().setType((byte) 3).setLength((short) ttlValue.length).setValue(ttlValue));
639 lldp.getOptionalTLVList().add(dpidTLV);
640
641 // Add the controller identifier to the TLV value.
642 lldp.getOptionalTLVList().add(controllerTLV);
643 if (isReverse) {
644 lldp.getOptionalTLVList().add(reverseTLV);
645 }else {
646 lldp.getOptionalTLVList().add(forwardTLV);
647 }
648
649 Ethernet ethernet;
650 if (isStandard) {
651 ethernet = new Ethernet()
652 .setSourceMACAddress(ofpPort.getHardwareAddress())
653 .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
654 .setEtherType(Ethernet.TYPE_LLDP);
655 ethernet.setPayload(lldp);
656 } else {
657 BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
658 bsn.setPayload(lldp);
659
660 ethernet = new Ethernet()
661 .setSourceMACAddress(ofpPort.getHardwareAddress())
662 .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
663 .setEtherType(Ethernet.TYPE_BSN);
664 ethernet.setPayload(bsn);
665 }
666
667
668 // serialize and wrap in a packet out
669 byte[] data = ethernet.serialize();
670 OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
671 po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
672 po.setInPort(OFPort.OFPP_NONE);
673
674 // set actions
675 List<OFAction> actions = new ArrayList<OFAction>();
676 actions.add(new OFActionOutput(port, (short) 0));
677 po.setActions(actions);
678 po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
679
680 // set data
681 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + data.length);
682 po.setPacketData(data);
683
684 // send
685 try {
686 iofSwitch.write(po, null);
687 iofSwitch.flush();
688 } catch (IOException e) {
689 log.error("Failure sending LLDP out port {} on switch {}",
690 new Object[]{ port, iofSwitch.getStringId() }, e);
691 }
692
693 }
694
695 /**
696 * Send LLDPs to all switch-ports
697 */
698 protected void discoverOnAllPorts() {
699 if (log.isTraceEnabled()) {
700 log.trace("Sending LLDP packets out of all the enabled ports on switch {}");
701 }
702 Set<Long> switches = floodlightProvider.getSwitches().keySet();
703 // Send standard LLDPs
704 for (long sw: switches) {
705 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
706 if (iofSwitch == null) continue;
707 if (iofSwitch.getEnabledPorts() != null) {
708 for (OFPhysicalPort ofp: iofSwitch.getEnabledPorts()) {
709 if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber()))
710 continue;
711 if (autoPortFastFeature && isFastPort(sw, ofp.getPortNumber()))
712 continue;
713
714 // sends forward LLDP only non-fastports.
715 sendDiscoveryMessage(sw, ofp.getPortNumber(), true, false);
716
717 // If the switch port is not alreayd in the maintenance
718 // queue, add it.
719 NodePortTuple npt = new NodePortTuple(sw, ofp.getPortNumber());
720 addToMaintenanceQueue(npt);
721 }
722 }
723 }
724 }
725
726 protected void setControllerTLV() {
727 //Setting the controllerTLVValue based on current nano time,
728 //controller's IP address, and the network interface object hash
729 //the corresponding IP address.
730
731 final int prime = 7867;
732 InetAddress localIPAddress = null;
733 NetworkInterface localInterface = null;
734
735 byte[] controllerTLVValue = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; // 8 byte value.
736 ByteBuffer bb = ByteBuffer.allocate(10);
737
738 try{
739 localIPAddress = java.net.InetAddress.getLocalHost();
740 localInterface = NetworkInterface.getByInetAddress(localIPAddress);
741 } catch (Exception e) {
742 e.printStackTrace();
743 }
744
745 long result = System.nanoTime();
746 if (localIPAddress != null)
747 result = result * prime + IPv4.toIPv4Address(localIPAddress.getHostAddress());
748 if (localInterface != null)
749 result = result * prime + localInterface.hashCode();
750 // set the first 4 bits to 0.
751 result = result & (0x0fffffffffffffffL);
752
753 bb.putLong(result);
754
755 bb.rewind();
756 bb.get(controllerTLVValue, 0, 8);
757
758 this.controllerTLV = new LLDPTLV().setType((byte) 0x0c).setLength((short) controllerTLVValue.length).setValue(controllerTLVValue);
759 }
760
761 @Override
762 public String getName() {
763 return "linkdiscovery";
764 }
765
766 @Override
767 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
768 switch (msg.getType()) {
769 case PACKET_IN:
770 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx);
771 case PORT_STATUS:
772 return this.handlePortStatus(sw.getId(), (OFPortStatus) msg);
773 default:
774 break;
775 }
776 return Command.CONTINUE;
777 }
778
779 private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, boolean isStandard, FloodlightContext cntx) {
780 // If LLDP is suppressed on this port, ignore received packet as well
781 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
782 if (iofSwitch == null) {
783 return Command.STOP;
784 }
785
786 if (isLinkDiscoverySuppressed(sw, pi.getInPort()))
787 return Command.STOP;
788
789 // If this is a malformed LLDP, or not from us, exit
790 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3)
791 return Command.CONTINUE;
792
793 long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
794 long otherId = 0;
795 boolean myLLDP = false;
796 Boolean isReverse = null;
797
798 ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
799 portBB.position(1);
800
801 Short remotePort = portBB.getShort();
802 IOFSwitch remoteSwitch = null;
803
804 // Verify this LLDP packet matches what we're looking for
805 for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
806 if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 &&
807 lldptlv.getValue()[0] == 0x0 && lldptlv.getValue()[1] == 0x26 &&
808 lldptlv.getValue()[2] == (byte)0xe1 && lldptlv.getValue()[3] == 0x0) {
809 ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue());
810 remoteSwitch = floodlightProvider.getSwitches().get(dpidBB.getLong(4));
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800811 if (remoteSwitch == null) {
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700812 // Added by ONOS
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800813 // floodlight LLDP coming from a remote switch connected to a different controller
814 // add it to our cache of unconnected remote switches
815 remoteSwitch = addRemoteSwitch(dpidBB.getLong(4), remotePort);
816 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800817 } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8){
818 otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
819 if (myId == otherId)
820 myLLDP = true;
821 } else if (lldptlv.getType() == TLV_DIRECTION_TYPE &&
822 lldptlv.getLength() == TLV_DIRECTION_LENGTH) {
823 if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0])
824 isReverse = false;
825 else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0])
826 isReverse = true;
827 }
828 }
829
830 if (myLLDP == false) {
831 // This is not the LLDP sent by this controller.
832 // If the LLDP message has multicast bit set, then we need to broadcast
833 // the packet as a regular packet.
834 if (isStandard) {
835 if (log.isTraceEnabled()) {
836 log.trace("Getting standard LLDP from a different controller and quelching it.");
837 }
838 return Command.STOP;
839 }
840 else if (myId < otherId) {
841 if (log.isTraceEnabled()) {
842 log.trace("Getting BDDP packets from a different controller" +
843 "and letting it go through normal processing chain.");
844 }
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700845 //XXX ONOS: Fix the BDDP broadcast issue
Jonathan Hart0b2c76a2013-02-27 17:09:33 -0800846 //return Command.CONTINUE;
847 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800848 }
849 }
850
851
852 if (remoteSwitch == null) {
853 // Ignore LLDPs not generated by Floodlight, or from a switch that has recently
854 // disconnected, or from a switch connected to another Floodlight instance
855 if (log.isTraceEnabled()) {
856 log.trace("Received LLDP from remote switch not connected to the controller");
857 }
858 return Command.STOP;
859 }
860
861 if (!remoteSwitch.portEnabled(remotePort)) {
862 if (log.isTraceEnabled()) {
863 log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
864 }
865 return Command.STOP;
866 }
867 if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
868 remotePort))) {
869 if (log.isTraceEnabled()) {
870 log.trace("Ignoring link with suppressed src port: switch {} port {}",
871 remoteSwitch, remotePort);
872 }
873 return Command.STOP;
874 }
875 if (!iofSwitch.portEnabled(pi.getInPort())) {
876 if (log.isTraceEnabled()) {
877 log.trace("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort());
878 }
879 return Command.STOP;
880 }
881
882 OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
883 int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
884 physicalPort = iofSwitch.getPort(pi.getInPort());
885 int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
886
887 // Store the time of update to this link, and push it out to routingEngine
888 Link lt = new Link(remoteSwitch.getId(), remotePort, iofSwitch.getId(), pi.getInPort());
889
890
891 Long lastLldpTime = null;
892 Long lastBddpTime = null;
893
894 Long firstSeenTime = System.currentTimeMillis();
895
896 if (isStandard)
897 lastLldpTime = System.currentTimeMillis();
898 else
899 lastBddpTime = System.currentTimeMillis();
900
901 LinkInfo newLinkInfo =
902 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
903 srcPortState, dstPortState);
904
905 addOrUpdateLink(lt, newLinkInfo);
906
907 // Check if reverse link exists.
908 // If it doesn't exist and if the forward link was seen
909 // first seen within a small interval, send probe on the
910 // reverse link.
911
912 newLinkInfo = links.get(lt);
913 if (newLinkInfo != null && isStandard && isReverse == false) {
914 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
915 lt.getSrc(), lt.getSrcPort());
916 LinkInfo reverseInfo = links.get(reverseLink);
917 if (reverseInfo == null) {
918 // the reverse link does not exist.
919 if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - LINK_TIMEOUT) {
920 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), isStandard, true);
921 }
922 }
923 }
924
925 // If the received packet is a BDDP packet, then create a reverse BDDP
926 // link as well.
927 if (!isStandard) {
928 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
929 lt.getSrc(), lt.getSrcPort());
930
931 // srcPortState and dstPort state are reversed.
932 LinkInfo reverseInfo =
933 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
934 dstPortState, srcPortState);
935
936 addOrUpdateLink(reverseLink, reverseInfo);
937 }
938
939 // Remove the node ports from the quarantine and maintenance queues.
940 NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
941 NodePortTuple nptDst = new NodePortTuple(lt.getDst(), lt.getDstPort());
942 removeFromQuarantineQueue(nptSrc);
943 removeFromMaintenanceQueue(nptSrc);
944 removeFromQuarantineQueue(nptDst);
945 removeFromMaintenanceQueue(nptDst);
946
947 // Consume this message
948 return Command.STOP;
949 }
950
951 protected Command handlePacketIn(long sw, OFPacketIn pi,
952 FloodlightContext cntx) {
953 Ethernet eth =
954 IFloodlightProviderService.bcStore.get(cntx,
955 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
956
957 if(eth.getEtherType() == Ethernet.TYPE_BSN) {
958 BSN bsn = (BSN) eth.getPayload();
959 if (bsn == null) return Command.STOP;
960 if (bsn.getPayload() == null) return Command.STOP;
961 // It could be a packet other than BSN LLDP, therefore
962 // continue with the regular processing.
963 if (bsn.getPayload() instanceof LLDP == false)
964 return Command.CONTINUE;
Ubuntu9cbb4ca2013-02-07 17:19:59 +0000965 return handleLldp((LLDP) bsn.getPayload(), sw, pi, false, cntx);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800966 } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
967 return handleLldp((LLDP) eth.getPayload(), sw, pi, true, cntx);
968 } else if (eth.getEtherType() < 1500) {
969 long destMac = eth.getDestinationMAC().toLong();
970 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE){
971 if (log.isTraceEnabled()) {
972 log.trace("Ignoring packet addressed to 802.1D/Q " +
973 "reserved address.");
974 }
975 return Command.STOP;
976 }
977 }
978
979 // If packet-in is from a quarantine port, stop processing.
980 NodePortTuple npt = new NodePortTuple(sw, pi.getInPort());
981 if (quarantineQueue.contains(npt)) return Command.STOP;
982
983 return Command.CONTINUE;
984 }
985
986 protected UpdateOperation getUpdateOperation(int srcPortState,
987 int dstPortState) {
988 boolean added =
989 (((srcPortState &
990 OFPortState.OFPPS_STP_MASK.getValue()) !=
991 OFPortState.OFPPS_STP_BLOCK.getValue()) &&
992 ((dstPortState &
993 OFPortState.OFPPS_STP_MASK.getValue()) !=
994 OFPortState.OFPPS_STP_BLOCK.getValue()));
995
996 if (added) return UpdateOperation.LINK_UPDATED;
997 return UpdateOperation.LINK_REMOVED;
998 }
999
1000
1001
1002 protected UpdateOperation getUpdateOperation(int srcPortState) {
1003 boolean portUp = ((srcPortState &
1004 OFPortState.OFPPS_STP_MASK.getValue()) !=
1005 OFPortState.OFPPS_STP_BLOCK.getValue());
1006
1007 if (portUp) return UpdateOperation.PORT_UP;
1008 else return UpdateOperation.PORT_DOWN;
1009 }
1010
1011 protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {
1012
1013 NodePortTuple srcNpt, dstNpt;
1014 boolean linkChanged = false;
1015
1016 lock.writeLock().lock();
1017 try {
1018 // put the new info. if an old info exists, it will be returned.
1019 LinkInfo oldInfo = links.put(lt, newInfo);
1020 if (oldInfo != null &&
1021 oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
1022 newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
1023
1024 if (log.isTraceEnabled()) {
1025 log.trace("addOrUpdateLink: {} {}",
1026 lt,
1027 (newInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast");
1028 }
1029
1030 UpdateOperation updateOperation = null;
1031 linkChanged = false;
1032
1033 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1034 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1035
1036 if (oldInfo == null) {
1037 // index it by switch source
1038 if (!switchLinks.containsKey(lt.getSrc()))
1039 switchLinks.put(lt.getSrc(), new HashSet<Link>());
1040 switchLinks.get(lt.getSrc()).add(lt);
1041
1042 // index it by switch dest
1043 if (!switchLinks.containsKey(lt.getDst()))
1044 switchLinks.put(lt.getDst(), new HashSet<Link>());
1045 switchLinks.get(lt.getDst()).add(lt);
1046
1047 // index both ends by switch:port
1048 if (!portLinks.containsKey(srcNpt))
1049 portLinks.put(srcNpt, new HashSet<Link>());
1050 portLinks.get(srcNpt).add(lt);
1051
1052 if (!portLinks.containsKey(dstNpt))
1053 portLinks.put(dstNpt, new HashSet<Link>());
1054 portLinks.get(dstNpt).add(lt);
1055
1056 // Add to portNOFLinks if the unicast valid time is null
1057 if (newInfo.getUnicastValidTime() == null)
1058 addLinkToBroadcastDomain(lt);
Umesh Krishnaswamy2b9d5642013-01-04 11:00:27 -08001059
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001060 // ONOS: Distinguish added event separately from updated event
Pankaj Berdea41016d2013-06-10 21:18:18 -07001061 updateOperation = UpdateOperation.LINK_ADDED;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001062 linkChanged = true;
1063
1064 // Add to event history
1065 evHistTopoLink(lt.getSrc(),
1066 lt.getDst(),
1067 lt.getSrcPort(),
1068 lt.getDstPort(),
1069 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1070 getLinkType(lt, newInfo),
1071 EvAction.LINK_ADDED, "LLDP Recvd");
1072 } else {
1073 // Since the link info is already there, we need to
1074 // update the right fields.
1075 if (newInfo.getUnicastValidTime() == null) {
1076 // This is due to a multicast LLDP, so copy the old unicast
1077 // value.
1078 if (oldInfo.getUnicastValidTime() != null) {
1079 newInfo.setUnicastValidTime(oldInfo.getUnicastValidTime());
1080 }
1081 } else if (newInfo.getMulticastValidTime() == null) {
1082 // This is due to a unicast LLDP, so copy the old multicast
1083 // value.
1084 if (oldInfo.getMulticastValidTime() != null) {
1085 newInfo.setMulticastValidTime(oldInfo.getMulticastValidTime());
1086 }
1087 }
1088
1089 Long oldTime = oldInfo.getUnicastValidTime();
1090 Long newTime = newInfo.getUnicastValidTime();
1091 // the link has changed its state between openflow and non-openflow
1092 // if the unicastValidTimes are null or not null
1093 if (oldTime != null & newTime == null) {
1094 // openflow -> non-openflow transition
1095 // we need to add the link tuple to the portNOFLinks
1096 addLinkToBroadcastDomain(lt);
1097 linkChanged = true;
1098 } else if (oldTime == null & newTime != null) {
1099 // non-openflow -> openflow transition
1100 // we need to remove the link from the portNOFLinks
1101 removeLinkFromBroadcastDomain(lt);
1102 linkChanged = true;
1103 }
1104
1105 // Only update the port states if they've changed
1106 if (newInfo.getSrcPortState().intValue() !=
1107 oldInfo.getSrcPortState().intValue() ||
1108 newInfo.getDstPortState().intValue() !=
1109 oldInfo.getDstPortState().intValue())
1110 linkChanged = true;
1111
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001112 if (linkChanged) {
1113 updateOperation = getUpdateOperation(newInfo.getSrcPortState(),
1114 newInfo.getDstPortState());
1115 if (log.isTraceEnabled()) {
1116 log.trace("Updated link {}", lt);
1117 }
1118 // Add to event history
1119 evHistTopoLink(lt.getSrc(),
1120 lt.getDst(),
1121 lt.getSrcPort(),
1122 lt.getDstPort(),
1123 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1124 getLinkType(lt, newInfo),
1125 EvAction.LINK_PORT_STATE_UPDATED,
1126 "LLDP Recvd");
1127 }
1128 }
1129
1130 if (linkChanged) {
1131 // find out if the link was added or removed here.
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001132 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001133 lt.getDst(), lt.getDstPort(),
1134 getLinkType(lt, newInfo),
1135 updateOperation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001136 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001137 }
1138 } finally {
1139 lock.writeLock().unlock();
1140 }
1141
1142 return linkChanged;
1143 }
1144
1145 public Map<Long, Set<Link>> getSwitchLinks() {
1146 return this.switchLinks;
1147 }
1148
1149 /**
1150 * Removes links from memory and storage.
1151 * @param links The List of @LinkTuple to delete.
1152 */
1153 protected void deleteLinks(List<Link> links, String reason) {
1154 NodePortTuple srcNpt, dstNpt;
1155
1156 lock.writeLock().lock();
1157 try {
1158 for (Link lt : links) {
1159 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1160 dstNpt =new NodePortTuple(lt.getDst(), lt.getDstPort());
1161
1162 switchLinks.get(lt.getSrc()).remove(lt);
1163 switchLinks.get(lt.getDst()).remove(lt);
1164 if (switchLinks.containsKey(lt.getSrc()) &&
1165 switchLinks.get(lt.getSrc()).isEmpty())
1166 this.switchLinks.remove(lt.getSrc());
1167 if (this.switchLinks.containsKey(lt.getDst()) &&
1168 this.switchLinks.get(lt.getDst()).isEmpty())
1169 this.switchLinks.remove(lt.getDst());
1170
1171 if (this.portLinks.get(srcNpt) != null) {
1172 this.portLinks.get(srcNpt).remove(lt);
1173 if (this.portLinks.get(srcNpt).isEmpty())
1174 this.portLinks.remove(srcNpt);
1175 }
1176 if (this.portLinks.get(dstNpt) != null) {
1177 this.portLinks.get(dstNpt).remove(lt);
1178 if (this.portLinks.get(dstNpt).isEmpty())
1179 this.portLinks.remove(dstNpt);
1180 }
1181
1182 LinkInfo info = this.links.remove(lt);
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001183 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001184 lt.getDst(), lt.getDstPort(),
1185 getLinkType(lt, info),
1186 UpdateOperation.LINK_REMOVED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001187 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001188
1189 // Update Event History
1190 evHistTopoLink(lt.getSrc(),
1191 lt.getDst(),
1192 lt.getSrcPort(),
1193 lt.getDstPort(),
1194 0, 0, // Port states
1195 ILinkDiscovery.LinkType.INVALID_LINK,
1196 EvAction.LINK_DELETED, reason);
1197
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001198 // TODO Whenever link is removed, it has to checked if
1199 // the switchports must be added to quarantine.
1200
1201 if (log.isTraceEnabled()) {
1202 log.trace("Deleted link {}", lt);
1203 }
1204 }
1205 } finally {
1206 lock.writeLock().unlock();
1207 }
1208 }
1209
1210 /**
1211 * Handles an OFPortStatus message from a switch. We will add or
1212 * delete LinkTupes as well re-compute the topology if needed.
1213 * @param sw The IOFSwitch that sent the port status message
1214 * @param ps The OFPortStatus message
1215 * @return The Command to continue or stop after we process this message
1216 */
1217 protected Command handlePortStatus(long sw, OFPortStatus ps) {
1218
1219 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
1220 if (iofSwitch == null) return Command.CONTINUE;
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -07001221
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001222 // ONOS: If we do not control this switch, then we should not process its port status messages
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001223 if (!registryService.hasControl(iofSwitch.getId())) return Command.CONTINUE;
1224
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001225 if (log.isTraceEnabled()) {
1226 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
1227 "config is {} state is {}",
1228 new Object[] {iofSwitch.getStringId(),
1229 ps.getDesc().getPortNumber(),
1230 ps.getReason(),
1231 ps.getDesc().getConfig(),
1232 ps.getDesc().getState()});
1233 }
1234
1235 short port = ps.getDesc().getPortNumber();
1236 NodePortTuple npt = new NodePortTuple(sw, port);
1237 boolean linkDeleted = false;
1238 boolean linkInfoChanged = false;
1239
1240 lock.writeLock().lock();
1241 try {
1242 // if ps is a delete, or a modify where the port is down or
1243 // configured down
1244 if ((byte)OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
1245 ((byte)OFPortReason.OFPPR_MODIFY.ordinal() ==
1246 ps.getReason() && !portEnabled(ps.getDesc()))) {
1247 deleteLinksOnPort(npt, "Port Status Changed");
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001248 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, UpdateOperation.PORT_DOWN));
1249 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001250 linkDeleted = true;
1251 }
1252 else if (ps.getReason() ==
1253 (byte)OFPortReason.OFPPR_MODIFY.ordinal()) {
1254 // If ps is a port modification and the port state has changed
1255 // that affects links in the topology
1256
1257 if (this.portLinks.containsKey(npt)) {
1258 for (Link lt: this.portLinks.get(npt)) {
1259 LinkInfo linkInfo = links.get(lt);
1260 assert(linkInfo != null);
1261 Integer updatedSrcPortState = null;
1262 Integer updatedDstPortState = null;
1263 if (lt.getSrc() == npt.getNodeId() &&
1264 lt.getSrcPort() == npt.getPortId() &&
1265 (linkInfo.getSrcPortState() !=
1266 ps.getDesc().getState())) {
1267 updatedSrcPortState = ps.getDesc().getState();
1268 linkInfo.setSrcPortState(updatedSrcPortState);
1269 }
1270 if (lt.getDst() == npt.getNodeId() &&
1271 lt.getDstPort() == npt.getPortId() &&
1272 (linkInfo.getDstPortState() !=
1273 ps.getDesc().getState())) {
1274 updatedDstPortState = ps.getDesc().getState();
1275 linkInfo.setDstPortState(updatedDstPortState);
1276 }
1277 if ((updatedSrcPortState != null) ||
1278 (updatedDstPortState != null)) {
1279 // The link is already known to link discovery
1280 // manager and the status has changed, therefore
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001281 // send an LinkUpdate.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001282 UpdateOperation operation =
1283 getUpdateOperation(linkInfo.getSrcPortState(),
1284 linkInfo.getDstPortState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001285 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001286 lt.getDst(), lt.getDstPort(),
1287 getLinkType(lt, linkInfo),
1288 operation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001289 controller.publishUpdate(update);
Jonathan Hart2fa28062013-11-25 20:16:28 -08001290
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001291 linkInfoChanged = true;
1292 }
1293 }
1294 }
1295
1296 UpdateOperation operation =
1297 getUpdateOperation(ps.getDesc().getState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001298 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
1299 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001300 }
1301
1302 if (!linkDeleted && !linkInfoChanged){
1303 if (log.isTraceEnabled()) {
1304 log.trace("handlePortStatus: Switch {} port #{} reason {};"+
1305 " no links to update/remove",
1306 new Object[] {HexString.toHexString(sw),
1307 ps.getDesc().getPortNumber(),
1308 ps.getReason()});
1309 }
1310 }
1311 } finally {
1312 lock.writeLock().unlock();
1313 }
1314
1315 if (!linkDeleted) {
1316 // Send LLDP right away when port state is changed for faster
1317 // cluster-merge. If it is a link delete then there is not need
1318 // to send the LLDPs right away and instead we wait for the LLDPs
1319 // to be sent on the timer as it is normally done
1320 // do it outside the write-lock
1321 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
1322 processNewPort(npt.getNodeId(), npt.getPortId());
1323 }
1324 return Command.CONTINUE;
1325 }
1326
1327 /**
1328 * Process a new port.
1329 * If link discovery is disabled on the port, then do nothing.
1330 * If autoportfast feature is enabled and the port is a fast port, then
1331 * do nothing.
1332 * Otherwise, send LLDP message. Add the port to quarantine.
1333 * @param sw
1334 * @param p
1335 */
1336 private void processNewPort(long sw, short p) {
1337 if (isLinkDiscoverySuppressed(sw, p)) {
1338 // Do nothing as link discovery is suppressed.
1339 }
1340 else if (autoPortFastFeature && isFastPort(sw, p)) {
1341 // Do nothing as the port is a fast port.
1342 }
1343 else {
1344 NodePortTuple npt = new NodePortTuple(sw, p);
1345 discover(sw, p);
1346 // if it is not a fast port, add it to quarantine.
1347 if (!isFastPort(sw, p)) {
1348 addToQuarantineQueue(npt);
1349 } else {
1350 // Add to maintenance queue to ensure that BDDP packets
1351 // are sent out.
1352 addToMaintenanceQueue(npt);
1353 }
1354 }
1355 }
1356
1357 /**
1358 * We send out LLDP messages when a switch is added to discover the topology
1359 * @param sw The IOFSwitch that connected to the controller
1360 */
1361 @Override
1362 public void addedSwitch(IOFSwitch sw) {
1363
1364 if (sw.getEnabledPorts() != null) {
1365 for (Short p : sw.getEnabledPortNumbers()) {
1366 processNewPort(sw.getId(), p);
1367 }
1368 }
1369 // Update event history
1370 evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001371 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), null,
1372 UpdateOperation.SWITCH_UPDATED));
1373 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001374 }
1375
1376 /**
1377 * When a switch disconnects we remove any links from our map and notify.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001378 */
1379 @Override
1380 public void removedSwitch(IOFSwitch iofSwitch) {
1381 // Update event history
1382 long sw = iofSwitch.getId();
1383 evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None");
1384 List<Link> eraseList = new ArrayList<Link>();
1385 lock.writeLock().lock();
1386 try {
1387 if (switchLinks.containsKey(sw)) {
1388 if (log.isTraceEnabled()) {
1389 log.trace("Handle switchRemoved. Switch {}; removing links {}",
1390 HexString.toHexString(sw), switchLinks.get(sw));
1391 }
1392 // add all tuples with an endpoint on this switch to erase list
1393 eraseList.addAll(switchLinks.get(sw));
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -07001394 deleteLinks(eraseList, "Switch Removed");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001395
1396 // Send a switch removed update
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001397 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED));
1398 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001399 }
1400 } finally {
1401 lock.writeLock().unlock();
1402 }
1403 }
1404
1405 /**
1406 * We don't react the port changed notifications here. we listen for
1407 * OFPortStatus messages directly. Might consider using this notifier
1408 * instead
1409 */
1410 @Override
1411 public void switchPortChanged(Long switchId) {
1412 // no-op
1413 }
1414
1415 /**
1416 * Delete links incident on a given switch port.
1417 * @param npt
1418 * @param reason
1419 */
1420 protected void deleteLinksOnPort(NodePortTuple npt, String reason) {
1421 List<Link> eraseList = new ArrayList<Link>();
1422 if (this.portLinks.containsKey(npt)) {
1423 if (log.isTraceEnabled()) {
1424 log.trace("handlePortStatus: Switch {} port #{} " +
1425 "removing links {}",
1426 new Object[] {HexString.toHexString(npt.getNodeId()),
1427 npt.getPortId(),
1428 this.portLinks.get(npt)});
1429 }
1430 eraseList.addAll(this.portLinks.get(npt));
1431 deleteLinks(eraseList, reason);
1432 }
1433 }
1434
1435 /**
1436 * Iterates through the list of links and deletes if the
1437 * last discovery message reception time exceeds timeout values.
1438 */
1439 protected void timeoutLinks() {
1440 List<Link> eraseList = new ArrayList<Link>();
1441 Long curTime = System.currentTimeMillis();
1442 boolean linkChanged = false;
1443
1444 // reentrant required here because deleteLink also write locks
1445 lock.writeLock().lock();
1446 try {
1447 Iterator<Entry<Link, LinkInfo>> it =
1448 this.links.entrySet().iterator();
1449 while (it.hasNext()) {
1450 Entry<Link, LinkInfo> entry = it.next();
1451 Link lt = entry.getKey();
1452 LinkInfo info = entry.getValue();
1453
1454 // Timeout the unicast and multicast LLDP valid times
1455 // independently.
1456 if ((info.getUnicastValidTime() != null) &&
1457 (info.getUnicastValidTime() + (this.LINK_TIMEOUT * 1000) < curTime)){
1458 info.setUnicastValidTime(null);
1459
1460 if (info.getMulticastValidTime() != null)
1461 addLinkToBroadcastDomain(lt);
1462 // Note that even if mTime becomes null later on,
1463 // the link would be deleted, which would trigger updateClusters().
1464 linkChanged = true;
1465 }
1466 if ((info.getMulticastValidTime()!= null) &&
1467 (info.getMulticastValidTime()+ (this.LINK_TIMEOUT * 1000) < curTime)) {
1468 info.setMulticastValidTime(null);
1469 // if uTime is not null, then link will remain as openflow
1470 // link. If uTime is null, it will be deleted. So, we
1471 // don't care about linkChanged flag here.
1472 removeLinkFromBroadcastDomain(lt);
1473 linkChanged = true;
1474 }
1475 // Add to the erase list only if the unicast
1476 // time is null.
1477 if (info.getUnicastValidTime() == null &&
1478 info.getMulticastValidTime() == null){
1479 eraseList.add(entry.getKey());
1480 } else if (linkChanged) {
1481 UpdateOperation operation;
1482 operation = getUpdateOperation(info.getSrcPortState(),
1483 info.getDstPortState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001484 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001485 lt.getDst(), lt.getDstPort(),
1486 getLinkType(lt, info),
1487 operation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001488 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001489 }
1490 }
1491
1492 // if any link was deleted or any link was changed.
1493 if ((eraseList.size() > 0) || linkChanged) {
1494 deleteLinks(eraseList, "LLDP timeout");
1495 }
1496 } finally {
1497 lock.writeLock().unlock();
1498 }
1499 }
1500
1501 private boolean portEnabled(OFPhysicalPort port) {
1502 if (port == null)
1503 return false;
1504 if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0)
1505 return false;
1506 if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0)
1507 return false;
1508 // Port STP state doesn't work with multiple VLANs, so ignore it for now
1509 // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
1510 // return false;
1511 return true;
1512 }
1513
1514 public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
1515 return portBroadcastDomainLinks;
1516 }
1517
1518 @Override
1519 public Map<Link, LinkInfo> getLinks() {
1520 lock.readLock().lock();
1521 Map<Link, LinkInfo> result;
1522 try {
1523 result = new HashMap<Link, LinkInfo>(links);
1524 } finally {
1525 lock.readLock().unlock();
1526 }
1527 return result;
1528 }
1529
1530 protected void addLinkToBroadcastDomain(Link lt) {
1531
1532 NodePortTuple srcNpt, dstNpt;
1533 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1534 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1535
1536 if (!portBroadcastDomainLinks.containsKey(lt.getSrc()))
1537 portBroadcastDomainLinks.put(srcNpt, new HashSet<Link>());
1538 portBroadcastDomainLinks.get(srcNpt).add(lt);
1539
1540 if (!portBroadcastDomainLinks.containsKey(lt.getDst()))
1541 portBroadcastDomainLinks.put(dstNpt, new HashSet<Link>());
1542 portBroadcastDomainLinks.get(dstNpt).add(lt);
1543 }
1544
1545 protected void removeLinkFromBroadcastDomain(Link lt) {
1546
1547 NodePortTuple srcNpt, dstNpt;
1548 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1549 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1550
1551 if (portBroadcastDomainLinks.containsKey(srcNpt)) {
1552 portBroadcastDomainLinks.get(srcNpt).remove(lt);
1553 if (portBroadcastDomainLinks.get(srcNpt).isEmpty())
1554 portBroadcastDomainLinks.remove(srcNpt);
1555 }
1556
1557 if (portBroadcastDomainLinks.containsKey(dstNpt)) {
1558 portBroadcastDomainLinks.get(dstNpt).remove(lt);
1559 if (portBroadcastDomainLinks.get(dstNpt).isEmpty())
1560 portBroadcastDomainLinks.remove(dstNpt);
1561 }
1562 }
1563
1564 // STORAGE METHODS
1565 /**
1566 * Deletes all links from storage
1567 */
Jonathan Hart2fa28062013-11-25 20:16:28 -08001568 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001569 void clearAllLinks() {
1570 storageSource.deleteRowsAsync(LINK_TABLE_NAME, null);
1571 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001572 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001573
1574 /**
1575 * Gets the storage key for a LinkTuple
1576 * @param lt The LinkTuple to get
1577 * @return The storage key as a String
1578 */
1579 private String getLinkId(Link lt) {
1580 return HexString.toHexString(lt.getSrc()) +
1581 "-" + lt.getSrcPort() + "-" +
1582 HexString.toHexString(lt.getDst())+
1583 "-" + lt.getDstPort();
1584 }
1585
1586 /**
1587 * Writes a LinkTuple and corresponding LinkInfo to storage
1588 * @param lt The LinkTuple to write
1589 * @param linkInfo The LinkInfo to write
1590 */
Jonathan Hart2fa28062013-11-25 20:16:28 -08001591 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001592 protected void writeLinkToStorage(Link lt, LinkInfo linkInfo) {
1593 LinkType type = getLinkType(lt, linkInfo);
1594
1595 // Write only direct links. Do not write links to external
1596 // L2 network.
1597 // if (type != LinkType.DIRECT_LINK && type != LinkType.TUNNEL) {
1598 // return;
1599 // }
1600
1601 Map<String, Object> rowValues = new HashMap<String, Object>();
1602 String id = getLinkId(lt);
1603 rowValues.put(LINK_ID, id);
1604 rowValues.put(LINK_VALID_TIME, linkInfo.getUnicastValidTime());
1605 String srcDpid = HexString.toHexString(lt.getSrc());
1606 rowValues.put(LINK_SRC_SWITCH, srcDpid);
1607 rowValues.put(LINK_SRC_PORT, lt.getSrcPort());
1608
1609 if (type == LinkType.DIRECT_LINK)
1610 rowValues.put(LINK_TYPE, "internal");
1611 else if (type == LinkType.MULTIHOP_LINK)
1612 rowValues.put(LINK_TYPE, "external");
1613 else if (type == LinkType.TUNNEL)
1614 rowValues.put(LINK_TYPE, "tunnel");
1615 else rowValues.put(LINK_TYPE, "invalid");
1616
1617 if (linkInfo.linkStpBlocked()) {
1618 if (log.isTraceEnabled()) {
1619 log.trace("writeLink, link {}, info {}, srcPortState Blocked",
1620 lt, linkInfo);
1621 }
1622 rowValues.put(LINK_SRC_PORT_STATE,
1623 OFPhysicalPort.OFPortState.OFPPS_STP_BLOCK.getValue());
1624 } else {
1625 if (log.isTraceEnabled()) {
1626 log.trace("writeLink, link {}, info {}, srcPortState {}",
1627 new Object[]{ lt, linkInfo, linkInfo.getSrcPortState() });
1628 }
1629 rowValues.put(LINK_SRC_PORT_STATE, linkInfo.getSrcPortState());
1630 }
1631 String dstDpid = HexString.toHexString(lt.getDst());
1632 rowValues.put(LINK_DST_SWITCH, dstDpid);
1633 rowValues.put(LINK_DST_PORT, lt.getDstPort());
1634 if (linkInfo.linkStpBlocked()) {
1635 if (log.isTraceEnabled()) {
1636 log.trace("writeLink, link {}, info {}, dstPortState Blocked",
1637 lt, linkInfo);
1638 }
1639 rowValues.put(LINK_DST_PORT_STATE,
1640 OFPhysicalPort.OFPortState.OFPPS_STP_BLOCK.getValue());
1641 } else {
1642 if (log.isTraceEnabled()) {
1643 log.trace("writeLink, link {}, info {}, dstPortState {}",
1644 new Object[]{ lt, linkInfo, linkInfo.getDstPortState() });
1645 }
1646 rowValues.put(LINK_DST_PORT_STATE, linkInfo.getDstPortState());
1647 }
1648 storageSource.updateRowAsync(LINK_TABLE_NAME, rowValues);
1649 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001650 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001651
1652 /**
1653 * Removes a link from storage using an asynchronous call.
1654 * @param lt The LinkTuple to delete.
1655 */
Jonathan Hart2fa28062013-11-25 20:16:28 -08001656 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001657 protected void removeLinkFromStorage(Link lt) {
1658 String id = getLinkId(lt);
1659 storageSource.deleteRowAsync(LINK_TABLE_NAME, id);
1660 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001661 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001662
1663 @Override
1664 public void addListener(ILinkDiscoveryListener listener) {
1665 linkDiscoveryAware.add(listener);
1666 }
1667
1668 /**
1669 * Register a link discovery aware component
1670 * @param linkDiscoveryAwareComponent
1671 */
1672 public void addLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
1673 // TODO make this a copy on write set or lock it somehow
1674 this.linkDiscoveryAware.add(linkDiscoveryAwareComponent);
1675 }
1676
1677 /**
1678 * Deregister a link discovery aware component
1679 * @param linkDiscoveryAwareComponent
1680 */
1681 public void removeLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
1682 // TODO make this a copy on write set or lock it somehow
1683 this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent);
1684 }
1685
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001686 @Override
1687 public boolean isCallbackOrderingPrereq(OFType type, String name) {
1688 return false;
1689 }
1690
1691 @Override
1692 public boolean isCallbackOrderingPostreq(OFType type, String name) {
1693 return false;
1694 }
1695
Jonathan Hart2fa28062013-11-25 20:16:28 -08001696 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001697 @Override
1698 public void rowsModified(String tableName, Set<Object> rowKeys) {
1699 Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches();
1700 ArrayList<IOFSwitch> updated_switches = new ArrayList<IOFSwitch>();
1701 for(Object key: rowKeys) {
1702 Long swId = new Long(HexString.toLong((String)key));
1703 if (switches.containsKey(swId)) {
1704 IOFSwitch sw = switches.get(swId);
1705 boolean curr_status = sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH);
1706 boolean new_status = false;
1707 IResultSet resultSet = null;
1708
1709 try {
1710 resultSet = storageSource.getRow(tableName, key);
1711 for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) {
1712 // In case of multiple rows, use the status in last row?
1713 Map<String, Object> row = it.next().getRow();
1714 if (row.containsKey(SWITCH_CONFIG_CORE_SWITCH)) {
1715 new_status = ((String)row.get(SWITCH_CONFIG_CORE_SWITCH)).equals("true");
1716 }
1717 }
1718 }
1719 finally {
1720 if (resultSet != null)
1721 resultSet.close();
1722 }
1723
1724 if (curr_status != new_status) {
1725 updated_switches.add(sw);
1726 }
1727 } else {
1728 if (log.isTraceEnabled()) {
1729 log.trace("Update for switch which has no entry in switch " +
1730 "list (dpid={}), a delete action.", (String)key);
1731 }
1732 }
1733 }
1734
1735 for (IOFSwitch sw : updated_switches) {
1736 // Set SWITCH_IS_CORE_SWITCH to it's inverse value
1737 if (sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH)) {
1738 sw.removeAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH);
1739 if (log.isTraceEnabled()) {
1740 log.trace("SWITCH_IS_CORE_SWITCH set to False for {}", sw);
1741 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001742 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), SwitchType.BASIC_SWITCH,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001743 UpdateOperation.SWITCH_UPDATED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001744 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001745 }
1746 else {
1747 sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, new Boolean(true));
1748 if (log.isTraceEnabled()) {
1749 log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw);
1750 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001751 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001752 UpdateOperation.SWITCH_UPDATED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001753 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001754 }
1755 }
1756 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001757 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001758
Jonathan Hart2fa28062013-11-25 20:16:28 -08001759 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001760 @Override
1761 public void rowsDeleted(String tableName, Set<Object> rowKeys) {
1762 // Ignore delete events, the switch delete will do the right thing on it's own
1763 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001764 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001765
1766 // IFloodlightModule classes
1767
1768 @Override
1769 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
1770 Collection<Class<? extends IFloodlightService>> l =
1771 new ArrayList<Class<? extends IFloodlightService>>();
1772 l.add(ILinkDiscoveryService.class);
1773 //l.add(ITopologyService.class);
1774 return l;
1775 }
1776
1777 @Override
1778 public Map<Class<? extends IFloodlightService>, IFloodlightService>
1779 getServiceImpls() {
1780 Map<Class<? extends IFloodlightService>,
1781 IFloodlightService> m =
1782 new HashMap<Class<? extends IFloodlightService>,
1783 IFloodlightService>();
1784 // We are the class that implements the service
1785 m.put(ILinkDiscoveryService.class, this);
1786 return m;
1787 }
1788
1789 @Override
1790 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
1791 Collection<Class<? extends IFloodlightService>> l =
1792 new ArrayList<Class<? extends IFloodlightService>>();
1793 l.add(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001794 l.add(IThreadPoolService.class);
1795 l.add(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001796 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001797 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001798 return l;
1799 }
1800
1801 @Override
1802 public void init(FloodlightModuleContext context)
1803 throws FloodlightModuleException {
1804 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001805 threadPool = context.getServiceImpl(IThreadPoolService.class);
1806 restApi = context.getServiceImpl(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001807 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001808 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001809
1810 // Set the autoportfast feature to false.
1811 this.autoPortFastFeature = false;
1812
1813 // We create this here because there is no ordering guarantee
1814 this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
1815 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001816 this.links = new HashMap<Link, LinkInfo>();
1817 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
1818 this.suppressLinkDiscovery =
1819 Collections.synchronizedSet(new HashSet<NodePortTuple>());
1820 this.portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
1821 this.switchLinks = new HashMap<Long, Set<Link>>();
1822 this.quarantineQueue = new LinkedBlockingQueue<NodePortTuple>();
1823 this.maintenanceQueue = new LinkedBlockingQueue<NodePortTuple>();
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001824 // Added by ONOS
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -07001825 this.remoteSwitches = new HashMap<Long, IOnosRemoteSwitch>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001826
1827 this.evHistTopologySwitch =
1828 new EventHistory<EventHistoryTopologySwitch>("Topology: Switch");
1829 this.evHistTopologyLink =
1830 new EventHistory<EventHistoryTopologyLink>("Topology: Link");
1831 this.evHistTopologyCluster =
1832 new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster");
1833 }
1834
1835 @Override
1836 @LogMessageDocs({
1837 @LogMessageDoc(level="ERROR",
1838 message="No storage source found.",
1839 explanation="Storage source was not initialized; cannot initialize " +
1840 "link discovery.",
1841 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1842 @LogMessageDoc(level="ERROR",
1843 message="Error in installing listener for " +
1844 "switch config table {table}",
1845 explanation="Failed to install storage notification for the " +
1846 "switch config table",
1847 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1848 @LogMessageDoc(level="ERROR",
1849 message="No storage source found.",
1850 explanation="Storage source was not initialized; cannot initialize " +
1851 "link discovery.",
1852 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1853 @LogMessageDoc(level="ERROR",
1854 message="Exception in LLDP send timer.",
1855 explanation="An unknown error occured while sending LLDP " +
1856 "messages to switches.",
1857 recommendation=LogMessageDoc.CHECK_SWITCH)
1858 })
1859 public void startUp(FloodlightModuleContext context) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001860 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001861 controller =
1862 context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001863
1864 // To be started by the first switch connection
1865 discoveryTask = new SingletonTask(ses, new Runnable() {
1866 @Override
1867 public void run() {
1868 try {
1869 discoverLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001870 } catch (Exception e) {
1871 log.error("Exception in LLDP send timer.", e);
1872 } finally {
1873 if (!shuttingDown) {
1874 // null role implies HA mode is not enabled.
1875 Role role = floodlightProvider.getRole();
1876 if (role == null || role == Role.MASTER) {
1877 log.trace("Rescheduling discovery task as role = {}", role);
1878 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
1879 TimeUnit.SECONDS);
1880 } else {
1881 log.trace("Stopped LLDP rescheduling due to role = {}.", role);
1882 }
1883 }
1884 }
1885 }
1886 });
1887
1888 // null role implies HA mode is not enabled.
1889 Role role = floodlightProvider.getRole();
1890 if (role == null || role == Role.MASTER) {
1891 log.trace("Setup: Rescheduling discovery task. role = {}", role);
1892 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
1893 } else {
1894 log.trace("Setup: Not scheduling LLDP as role = {}.", role);
1895 }
1896
1897 // Setup the BDDP task. It is invoked whenever switch port tuples
1898 // are added to the quarantine list.
1899 bddpTask = new SingletonTask(ses, new QuarantineWorker());
1900 bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
1901
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001902
1903 // Register for the OpenFlow messages we want to receive
1904 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1905 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1906 // Register for switch updates
1907 floodlightProvider.addOFSwitchListener(this);
1908 floodlightProvider.addHAListener(this);
1909 floodlightProvider.addInfoProvider("summary", this);
1910 if (restApi != null)
1911 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
1912 setControllerTLV();
1913 }
1914
1915 // ****************************************************
1916 // Topology Manager's Event History members and methods
1917 // ****************************************************
1918
1919 // Topology Manager event history
1920 public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch;
1921 public EventHistory<EventHistoryTopologyLink> evHistTopologyLink;
1922 public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster;
1923 public EventHistoryTopologySwitch evTopoSwitch;
1924 public EventHistoryTopologyLink evTopoLink;
1925 public EventHistoryTopologyCluster evTopoCluster;
1926
1927 // Switch Added/Deleted
1928 private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
1929 if (evTopoSwitch == null) {
1930 evTopoSwitch = new EventHistoryTopologySwitch();
1931 }
1932 evTopoSwitch.dpid = sw.getId();
1933 if ((sw.getChannel() != null) &&
1934 (SocketAddress.class.isInstance(
1935 sw.getChannel().getRemoteAddress()))) {
1936 evTopoSwitch.ipv4Addr =
1937 IPv4.toIPv4Address(((InetSocketAddress)(sw.getChannel().
1938 getRemoteAddress())).getAddress().getAddress());
1939 evTopoSwitch.l4Port =
1940 ((InetSocketAddress)(sw.getChannel().
1941 getRemoteAddress())).getPort();
1942 } else {
1943 evTopoSwitch.ipv4Addr = 0;
1944 evTopoSwitch.l4Port = 0;
1945 }
1946 evTopoSwitch.reason = reason;
1947 evTopoSwitch = evHistTopologySwitch.put(evTopoSwitch, actn);
1948 }
1949
1950 private void evHistTopoLink(long srcDpid, long dstDpid, short srcPort,
1951 short dstPort, int srcPortState, int dstPortState,
1952 ILinkDiscovery.LinkType linkType,
1953 EvAction actn, String reason) {
1954 if (evTopoLink == null) {
1955 evTopoLink = new EventHistoryTopologyLink();
1956 }
1957 evTopoLink.srcSwDpid = srcDpid;
1958 evTopoLink.dstSwDpid = dstDpid;
1959 evTopoLink.srcSwport = srcPort & 0xffff;
1960 evTopoLink.dstSwport = dstPort & 0xffff;
1961 evTopoLink.srcPortState = srcPortState;
1962 evTopoLink.dstPortState = dstPortState;
1963 evTopoLink.reason = reason;
1964 switch (linkType) {
1965 case DIRECT_LINK:
1966 evTopoLink.linkType = "DIRECT_LINK";
1967 break;
1968 case MULTIHOP_LINK:
1969 evTopoLink.linkType = "MULTIHOP_LINK";
1970 break;
1971 case TUNNEL:
1972 evTopoLink.linkType = "TUNNEL";
1973 break;
1974 case INVALID_LINK:
1975 default:
1976 evTopoLink.linkType = "Unknown";
1977 break;
1978 }
1979 evTopoLink = evHistTopologyLink.put(evTopoLink, actn);
1980 }
1981
1982 public void evHistTopoCluster(long dpid, long clusterIdOld,
1983 long clusterIdNew, EvAction action, String reason) {
1984 if (evTopoCluster == null) {
1985 evTopoCluster = new EventHistoryTopologyCluster();
1986 }
1987 evTopoCluster.dpid = dpid;
1988 evTopoCluster.clusterIdOld = clusterIdOld;
1989 evTopoCluster.clusterIdNew = clusterIdNew;
1990 evTopoCluster.reason = reason;
1991 evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action);
1992 }
1993
1994 @Override
1995 public Map<String, Object> getInfo(String type) {
1996 if (!"summary".equals(type)) return null;
1997
1998 Map<String, Object> info = new HashMap<String, Object>();
1999
2000 int num_links = 0;
2001 for (Set<Link> links : switchLinks.values())
2002 num_links += links.size();
2003 info.put("# inter-switch links", num_links / 2);
2004
2005 return info;
2006 }
2007
2008 // IHARoleListener
2009 @Override
2010 public void roleChanged(Role oldRole, Role newRole) {
2011 switch(newRole) {
2012 case MASTER:
2013 if (oldRole == Role.SLAVE) {
2014 if (log.isTraceEnabled()) {
2015 log.trace("Sending LLDPs " +
2016 "to HA change from SLAVE->MASTER");
2017 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08002018 //clearAllLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08002019 log.debug("Role Change to Master: Rescheduling discovery task.");
2020 discoveryTask.reschedule(1, TimeUnit.MICROSECONDS);
2021 }
2022 break;
2023 case SLAVE:
2024 if (log.isTraceEnabled()) {
2025 log.trace("Clearing links due to " +
2026 "HA change to SLAVE");
2027 }
2028 switchLinks.clear();
2029 links.clear();
2030 portLinks.clear();
2031 portBroadcastDomainLinks.clear();
2032 discoverOnAllPorts();
2033 break;
2034 default:
2035 break;
2036 }
2037 }
2038
2039 @Override
2040 public void controllerNodeIPsChanged(
2041 Map<String, String> curControllerNodeIPs,
2042 Map<String, String> addedControllerNodeIPs,
2043 Map<String, String> removedControllerNodeIPs) {
2044 // ignore
2045 }
2046
2047 public boolean isAutoPortFastFeature() {
2048 return autoPortFastFeature;
2049 }
2050
2051 public void setAutoPortFastFeature(boolean autoPortFastFeature) {
2052 this.autoPortFastFeature = autoPortFastFeature;
2053 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08002054}