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