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