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