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