blob: c03b266444a5cb0d69e366e2c1848c57017693ec [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2 * Copyright 2011, Big Switch Networks, Inc.
3 * Originally created by David Erickson, Stanford University
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain
7 * a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 **/
17
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070018package net.onrc.onos.ofcontroller.linkdiscovery.internal;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080019
20import java.io.IOException;
Pankaj Berdedc73bb12013-08-14 13:46:38 -070021
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080022import java.net.InetAddress;
23import java.net.InetSocketAddress;
24import java.net.NetworkInterface;
25import java.net.SocketAddress;
26import java.nio.ByteBuffer;
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.HashMap;
31import java.util.HashSet;
32import java.util.Iterator;
33import java.util.List;
34import java.util.Map;
35import java.util.Map.Entry;
36import java.util.Set;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080037import 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;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080059import net.floodlightcontroller.packet.BSN;
60import net.floodlightcontroller.packet.Ethernet;
61import net.floodlightcontroller.packet.IPv4;
62import net.floodlightcontroller.packet.LLDP;
63import net.floodlightcontroller.packet.LLDPTLV;
64import net.floodlightcontroller.restserver.IRestApiService;
65import net.floodlightcontroller.routing.Link;
66import net.floodlightcontroller.storage.IResultSet;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080067import net.floodlightcontroller.storage.IStorageSourceListener;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070068import net.floodlightcontroller.storage.IStorageSourceService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080069import net.floodlightcontroller.storage.OperatorPredicate;
70import net.floodlightcontroller.storage.StorageException;
71import net.floodlightcontroller.threadpool.IThreadPoolService;
72import net.floodlightcontroller.topology.NodePortTuple;
73import net.floodlightcontroller.util.EventHistory;
74import net.floodlightcontroller.util.EventHistory.EvAction;
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -070075import net.onrc.onos.ofcontroller.core.IOnosRemoteSwitch;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070076import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery;
77import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryListener;
78import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
79import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
80import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
81import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LinkType;
82import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.SwitchType;
83import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.UpdateOperation;
84import net.onrc.onos.ofcontroller.linkdiscovery.web.LinkDiscoveryWebRoutable;
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 {
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700129 protected IFloodlightProviderService controller;
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700130 protected final static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800131
132 // Names of table/fields for links in the storage API
133 private static final String LINK_TABLE_NAME = "controller_link";
134 private static final String LINK_ID = "id";
135 private static final String LINK_SRC_SWITCH = "src_switch_id";
136 private static final String LINK_SRC_PORT = "src_port";
137 private static final String LINK_SRC_PORT_STATE = "src_port_state";
138 private static final String LINK_DST_SWITCH = "dst_switch_id";
139 private static final String LINK_DST_PORT = "dst_port";
140 private static final String LINK_DST_PORT_STATE = "dst_port_state";
141 private static final String LINK_VALID_TIME = "valid_time";
142 private static final String LINK_TYPE = "link_type";
143 private static final String SWITCH_CONFIG_TABLE_NAME = "controller_switchconfig";
144 private static final String SWITCH_CONFIG_CORE_SWITCH = "core_switch";
145
146 protected IFloodlightProviderService floodlightProvider;
147 protected IStorageSourceService storageSource;
148 protected IThreadPoolService threadPool;
149 protected IRestApiService restApi;
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700150 // Registry Service for ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700151 protected IControllerRegistryService registryService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800152
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700153
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800154 // LLDP and BDDP fields
155 private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
156 HexString.fromHexString("01:80:c2:00:00:0e");
157 private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
158 private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
159
160 // BigSwitch OUI is 5C:16:C7, so 5D:16:C7 is the multicast version
Masayoshi Kobayashi71137362013-07-12 15:54:52 -0700161 // private static final String LLDP_BSN_DST_MAC_STRING = "5d:16:c7:00:00:01";
162 private static final String LLDP_BSN_DST_MAC_STRING = "ff:ff:ff:ff:ff:ff";
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800163
164
165 // Direction TLVs are used to indicate if the LLDPs were sent
166 // periodically or in response to a recieved LLDP
167 private static final byte TLV_DIRECTION_TYPE = 0x73;
168 private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte
169 private static final byte TLV_DIRECTION_VALUE_FORWARD[] = {0x01};
170 private static final byte TLV_DIRECTION_VALUE_REVERSE[] = {0x02};
171 private static final LLDPTLV forwardTLV
172 = new LLDPTLV().
173 setType((byte)TLV_DIRECTION_TYPE).
174 setLength((short)TLV_DIRECTION_LENGTH).
175 setValue(TLV_DIRECTION_VALUE_FORWARD);
176
177 private static final LLDPTLV reverseTLV
178 = new LLDPTLV().
179 setType((byte)TLV_DIRECTION_TYPE).
180 setLength((short)TLV_DIRECTION_LENGTH).
181 setValue(TLV_DIRECTION_VALUE_REVERSE);
182
183 // Link discovery task details.
184 protected SingletonTask discoveryTask;
185 protected final int DISCOVERY_TASK_INTERVAL = 1;
Umesh Krishnaswamy77601202013-04-03 21:46:01 -0700186 protected final int LINK_TIMEOUT = 35; // original 35 secs, aggressive 5 secs
187 protected final int LLDP_TO_ALL_INTERVAL = 15 ; //original 15 seconds, aggressive 2 secs.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800188 protected long lldpClock = 0;
189 // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
190 // If we want to identify link failures faster, we could decrease this
191 // value to a small number, say 1 or 2 sec.
Umesh Krishnaswamy77601202013-04-03 21:46:01 -0700192 protected final int LLDP_TO_KNOWN_INTERVAL= 20; // LLDP frequency for known links
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800193
194 protected LLDPTLV controllerTLV;
195 protected ReentrantReadWriteLock lock;
196 int lldpTimeCount = 0;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700197
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800198 /**
199 * Flag to indicate if automatic port fast is enabled or not.
200 * Default is set to false -- Initialized in the init method as well.
201 */
202 boolean autoPortFastFeature = false;
203
204 /**
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800205 * Map of remote switches that are not connected to this controller. This
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700206 * is used to learn remote switches in a distributed controller ONOS.
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800207 */
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -0700208 protected Map<Long, IOnosRemoteSwitch> remoteSwitches;
HIGUCHI Yuta3d96f652013-06-17 12:07:48 -0700209
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800210 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800211 * Map from link to the most recent time it was verified functioning
212 */
213 protected Map<Link, LinkInfo> links;
214
215 /**
216 * Map from switch id to a set of all links with it as an endpoint
217 */
218 protected Map<Long, Set<Link>> switchLinks;
219
220 /**
221 * Map from a id:port to the set of links containing it as an endpoint
222 */
223 protected Map<NodePortTuple, Set<Link>> portLinks;
224
225 /**
226 * Set of link tuples over which multicast LLDPs are received
227 * and unicast LLDPs are not received.
228 */
229 protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks;
230
231 protected volatile boolean shuttingDown = false;
232
233 /* topology aware components are called in the order they were added to the
234 * the array */
235 protected ArrayList<ILinkDiscoveryListener> linkDiscoveryAware;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700236
237 protected class LinkUpdate extends LDUpdate {
238
239 public LinkUpdate(LDUpdate old) {
240 super(old);
241 }
242 @LogMessageDoc(level="ERROR",
243 message="Error in link discovery updates loop",
244 explanation="An unknown error occured while dispatching " +
245 "link update notifications",
246 recommendation=LogMessageDoc.GENERIC_ACTION)
247 @Override
248 public void dispatch() {
249 if (linkDiscoveryAware != null) {
250 if (log.isTraceEnabled()) {
251 log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
252 new Object[]{this.getOperation(),
253 HexString.toHexString(this.getSrc()), this.getSrcPort(),
254 HexString.toHexString(this.getDst()), this.getDstPort(),
255 linkDiscoveryAware});
256 }
257 try {
258 for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained
259 lda.linkDiscoveryUpdate(this);
260 }
261 }
262 catch (Exception e) {
263 log.error("Error in link discovery updates loop", e);
264 }
265 }
266
267 }
268
269 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800270
271 /**
272 * List of ports through which LLDP/BDDPs are not sent.
273 */
274 protected Set<NodePortTuple> suppressLinkDiscovery;
275
276 /** A list of ports that are quarantined for discovering links through
277 * them. Data traffic from these ports are not allowed until the ports
278 * are released from quarantine.
279 */
280 protected LinkedBlockingQueue<NodePortTuple> quarantineQueue;
281 protected LinkedBlockingQueue<NodePortTuple> maintenanceQueue;
282 /**
283 * Quarantine task
284 */
285 protected SingletonTask bddpTask;
286 protected final int BDDP_TASK_INTERVAL = 100; // 100 ms.
287 protected final int BDDP_TASK_SIZE = 5; // # of ports per iteration
288
289 /**
290 * Map of broadcast domain ports and the last time a BDDP was either
291 * sent or received on that port.
292 */
293 protected Map<NodePortTuple, Long> broadcastDomainPortTimeMap;
294
295 /**
296 * Get the LLDP sending period in seconds.
297 * @return LLDP sending period in seconds.
298 */
299 public int getLldpFrequency() {
300 return LLDP_TO_KNOWN_INTERVAL;
301 }
302
303 /**
304 * Get the LLDP timeout value in seconds
305 * @return LLDP timeout value in seconds
306 */
307 public int getLldpTimeout() {
308 return LINK_TIMEOUT;
309 }
310
311 public Map<NodePortTuple, Set<Link>> getPortLinks() {
312 return portLinks;
313 }
314
315 public Set<NodePortTuple> getSuppressLLDPsInfo() {
316 return suppressLinkDiscovery;
317 }
318
319 /**
320 * Add a switch port to the suppressed LLDP list.
321 * Remove any known links on the switch port.
322 */
323 public void AddToSuppressLLDPs(long sw, short port)
324 {
325 NodePortTuple npt = new NodePortTuple(sw, port);
326 this.suppressLinkDiscovery.add(npt);
327 deleteLinksOnPort(npt, "LLDP suppressed.");
328 }
329
330 /**
331 * Remove a switch port from the suppressed LLDP list.
332 * Discover links on that switchport.
333 */
334 public void RemoveFromSuppressLLDPs(long sw, short port)
335 {
336 NodePortTuple npt = new NodePortTuple(sw, port);
337 this.suppressLinkDiscovery.remove(npt);
338 discover(npt);
339 }
340
341 public boolean isShuttingDown() {
342 return shuttingDown;
343 }
344
345 public boolean isFastPort(long sw, short port) {
346 return false;
347 }
348
349 public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
350 if (info.getUnicastValidTime() != null) {
351 return ILinkDiscovery.LinkType.DIRECT_LINK;
352 } else if (info.getMulticastValidTime() != null) {
353 return ILinkDiscovery.LinkType.MULTIHOP_LINK;
354 }
355 return ILinkDiscovery.LinkType.INVALID_LINK;
356 }
357
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700358
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800359 private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
360 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
361 }
362
363 protected void discoverLinks() {
364
365 // timeout known links.
366 timeoutLinks();
367
368 //increment LLDP clock
369 lldpClock = (lldpClock + 1)% LLDP_TO_ALL_INTERVAL;
370
371 if (lldpClock == 0) {
372 log.debug("Sending LLDP out on all ports.");
373 discoverOnAllPorts();
374 }
375 }
376
377
378 /**
379 * Quarantine Ports.
380 */
381 protected class QuarantineWorker implements Runnable {
382 @Override
383 public void run() {
384 try {
385 processBDDPLists();
386 }
387 catch (Exception e) {
388 log.error("Error in quarantine worker thread", e);
389 } finally {
390 bddpTask.reschedule(BDDP_TASK_INTERVAL,
391 TimeUnit.MILLISECONDS);
392 }
393 }
394 }
395
396 /**
397 * Add a switch port to the quarantine queue. Schedule the
398 * quarantine task if the quarantine queue was empty before adding
399 * this switch port.
400 * @param npt
401 */
402 protected void addToQuarantineQueue(NodePortTuple npt) {
403 if (quarantineQueue.contains(npt) == false)
404 quarantineQueue.add(npt);
405 }
406
407 /**
408 * Remove a switch port from the quarantine queue.
409 */
410 protected void removeFromQuarantineQueue(NodePortTuple npt) {
411 // Remove all occurrences of the node port tuple from the list.
412 while (quarantineQueue.remove(npt));
413 }
414
415 /**
416 * Add a switch port to maintenance queue.
417 * @param npt
418 */
419 protected void addToMaintenanceQueue(NodePortTuple npt) {
420 // TODO We are not checking if the switch port tuple is already
421 // in the maintenance list or not. This will be an issue for
422 // really large number of switch ports in the network.
423 if (maintenanceQueue.contains(npt) == false)
424 maintenanceQueue.add(npt);
425 }
426
427 /**
428 * Remove a switch port from maintenance queue.
429 * @param npt
430 */
431 protected void removeFromMaintenanceQueue(NodePortTuple npt) {
432 // Remove all occurrences of the node port tuple from the queue.
433 while (maintenanceQueue.remove(npt));
434 }
435
436 /**
437 * This method processes the quarantine list in bursts. The task is
438 * at most once per BDDP_TASK_INTERVAL.
439 * One each call, BDDP_TASK_SIZE number of switch ports are processed.
440 * Once the BDDP packets are sent out through the switch ports, the ports
441 * are removed from the quarantine list.
442 */
443
444 protected void processBDDPLists() {
445 int count = 0;
446 Set<NodePortTuple> nptList = new HashSet<NodePortTuple>();
447
448 while(count < BDDP_TASK_SIZE && quarantineQueue.peek() !=null) {
449 NodePortTuple npt;
450 npt = quarantineQueue.remove();
451 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
452 nptList.add(npt);
453 count++;
454 }
455
456 count = 0;
457 while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) {
458 NodePortTuple npt;
459 npt = maintenanceQueue.remove();
460 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
461 count++;
462 }
463
464 for(NodePortTuple npt:nptList) {
465 generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId());
466 }
467 }
468
469 public Set<Short> getQuarantinedPorts(long sw) {
470 Set<Short> qPorts = new HashSet<Short>();
471
472 Iterator<NodePortTuple> iter = quarantineQueue.iterator();
473 while (iter.hasNext()) {
474 NodePortTuple npt = iter.next();
475 if (npt.getNodeId() == sw) {
476 qPorts.add(npt.getPortId());
477 }
478 }
479 return qPorts;
480 }
481
482 private void generateSwitchPortStatusUpdate(long sw, short port) {
483 UpdateOperation operation;
484
485 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
486 if (iofSwitch == null) return;
487
488 OFPhysicalPort ofp = iofSwitch.getPort(port);
489 if (ofp == null) return;
490
491 int srcPortState = ofp.getState();
492 boolean portUp = ((srcPortState &
493 OFPortState.OFPPS_STP_MASK.getValue()) !=
494 OFPortState.OFPPS_STP_BLOCK.getValue());
495
496 if (portUp) operation = UpdateOperation.PORT_UP;
497 else operation = UpdateOperation.PORT_DOWN;
498
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700499 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
500
501
502 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800503 }
504
505 /**
506 * Send LLDP on known ports
507 */
508 protected void discoverOnKnownLinkPorts() {
509 // Copy the port set.
510 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
511 nptSet.addAll(portLinks.keySet());
512
513 // Send LLDP from each of them.
514 for(NodePortTuple npt: nptSet) {
515 discover(npt);
516 }
517 }
518
519 protected void discover(NodePortTuple npt) {
520 discover(npt.getNodeId(), npt.getPortId());
521 }
522
523 protected void discover(long sw, short port) {
524 sendDiscoveryMessage(sw, port, true, false);
525 }
526
527 /**
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700528 * Learn remote switches when running as a distributed controller ONOS
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800529 */
530 protected IOFSwitch addRemoteSwitch(long sw, short port) {
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -0700531 IOnosRemoteSwitch remotesw = null;
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800532
533 // add a switch if we have not seen it before
Umesh Krishnaswamy4c8e1082013-01-24 23:15:37 -0800534 remotesw = remoteSwitches.get(sw);
Jonathan Harte7231052013-01-25 00:01:14 -0800535
Pankaj Berdec125e622013-01-25 06:39:39 -0800536 if (remotesw == null) {
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800537 remotesw = new OFSwitchImpl();
538 remotesw.setupRemoteSwitch(sw);
539 remoteSwitches.put(remotesw.getId(), remotesw);
540 log.debug("addRemoteSwitch(): added fake remote sw {}", remotesw);
541 }
542
543 // add the port if we have not seen it before
Umesh Krishnaswamy68c118c2013-01-25 11:07:09 -0800544 if (remotesw.getPort(port) == null) {
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800545 OFPhysicalPort remoteport = new OFPhysicalPort();
546 remoteport.setPortNumber(port);
547 remoteport.setName("fake_" + port);
548 remoteport.setConfig(0);
549 remoteport.setState(0);
550 remotesw.setPort(remoteport);
Umesh Krishnaswamy82dcd982013-02-01 15:36:15 -0800551 log.debug("addRemoteSwitch(): added fake remote port {} to sw {}", remoteport, remotesw.getId());
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800552 }
553
554 return remotesw;
555 }
556
557 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800558 * Send link discovery message out of a given switch port.
559 * The discovery message may be a standard LLDP or a modified
560 * LLDP, where the dst mac address is set to :ff.
561 *
562 * TODO: The modified LLDP will updated in the future and may
563 * use a different eth-type.
564 * @param sw
565 * @param port
566 * @param isStandard indicates standard or modified LLDP
567 * @param isReverse indicates whether the LLDP was sent as a response
568 */
569 @LogMessageDoc(level="ERROR",
570 message="Failure sending LLDP out port {port} on switch {switch}",
571 explanation="An I/O error occured while sending LLDP message " +
572 "to the switch.",
573 recommendation=LogMessageDoc.CHECK_SWITCH)
574 protected void sendDiscoveryMessage(long sw, short port,
575 boolean isStandard,
576 boolean isReverse) {
577
578 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
579 if (iofSwitch == null) {
580 return;
581 }
582
583 if (port == OFPort.OFPP_LOCAL.getValue())
584 return;
585
586 OFPhysicalPort ofpPort = iofSwitch.getPort(port);
587
588 if (ofpPort == null) {
589 if (log.isTraceEnabled()) {
590 log.trace("Null physical port. sw={}, port={}", sw, port);
591 }
592 return;
593 }
594
595 if (isLinkDiscoverySuppressed(sw, port)) {
596 /* Dont send LLDPs out of this port as suppressLLDPs set
597 *
598 */
599 return;
600 }
601
602 // For fast ports, do not send forward LLDPs or BDDPs.
603 if (!isReverse && autoPortFastFeature && isFastPort(sw, port))
604 return;
605
606 if (log.isTraceEnabled()) {
607 log.trace("Sending LLDP packet out of swich: {}, port: {}",
608 sw, port);
609 }
610
611 // using "nearest customer bridge" MAC address for broadest possible propagation
612 // through provider and TPMR bridges (see IEEE 802.1AB-2009 and 802.1Q-2011),
613 // in particular the Linux bridge which behaves mostly like a provider bridge
614 byte[] chassisId = new byte[] {4, 0, 0, 0, 0, 0, 0}; // filled in later
615 byte[] portId = new byte[] {2, 0, 0}; // filled in later
616 byte[] ttlValue = new byte[] {0, 0x78};
617 // OpenFlow OUI - 00-26-E1
618 byte[] dpidTLVValue = new byte[] {0x0, 0x26, (byte) 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
619 LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127).setLength((short) dpidTLVValue.length).setValue(dpidTLVValue);
620
621 byte[] dpidArray = new byte[8];
622 ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
623 ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
624
625 Long dpid = sw;
626 dpidBB.putLong(dpid);
627 // set the ethernet source mac to last 6 bytes of dpid
628 System.arraycopy(dpidArray, 2, ofpPort.getHardwareAddress(), 0, 6);
629 // set the chassis id's value to last 6 bytes of dpid
630 System.arraycopy(dpidArray, 2, chassisId, 1, 6);
631 // set the optional tlv to the full dpid
632 System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
633
634
635 // set the portId to the outgoing port
636 portBB.putShort(port);
637 if (log.isTraceEnabled()) {
638 log.trace("Sending LLDP out of interface: {}/{}",
639 HexString.toHexString(sw), port);
640 }
641
642 LLDP lldp = new LLDP();
643 lldp.setChassisId(new LLDPTLV().setType((byte) 1).setLength((short) chassisId.length).setValue(chassisId));
644 lldp.setPortId(new LLDPTLV().setType((byte) 2).setLength((short) portId.length).setValue(portId));
645 lldp.setTtl(new LLDPTLV().setType((byte) 3).setLength((short) ttlValue.length).setValue(ttlValue));
646 lldp.getOptionalTLVList().add(dpidTLV);
647
648 // Add the controller identifier to the TLV value.
649 lldp.getOptionalTLVList().add(controllerTLV);
650 if (isReverse) {
651 lldp.getOptionalTLVList().add(reverseTLV);
652 }else {
653 lldp.getOptionalTLVList().add(forwardTLV);
654 }
655
656 Ethernet ethernet;
657 if (isStandard) {
658 ethernet = new Ethernet()
659 .setSourceMACAddress(ofpPort.getHardwareAddress())
660 .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
661 .setEtherType(Ethernet.TYPE_LLDP);
662 ethernet.setPayload(lldp);
663 } else {
664 BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
665 bsn.setPayload(lldp);
666
667 ethernet = new Ethernet()
668 .setSourceMACAddress(ofpPort.getHardwareAddress())
669 .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
670 .setEtherType(Ethernet.TYPE_BSN);
671 ethernet.setPayload(bsn);
672 }
673
674
675 // serialize and wrap in a packet out
676 byte[] data = ethernet.serialize();
677 OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
678 po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
679 po.setInPort(OFPort.OFPP_NONE);
680
681 // set actions
682 List<OFAction> actions = new ArrayList<OFAction>();
683 actions.add(new OFActionOutput(port, (short) 0));
684 po.setActions(actions);
685 po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
686
687 // set data
688 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + data.length);
689 po.setPacketData(data);
690
691 // send
692 try {
693 iofSwitch.write(po, null);
694 iofSwitch.flush();
695 } catch (IOException e) {
696 log.error("Failure sending LLDP out port {} on switch {}",
697 new Object[]{ port, iofSwitch.getStringId() }, e);
698 }
699
700 }
701
702 /**
703 * Send LLDPs to all switch-ports
704 */
705 protected void discoverOnAllPorts() {
706 if (log.isTraceEnabled()) {
707 log.trace("Sending LLDP packets out of all the enabled ports on switch {}");
708 }
709 Set<Long> switches = floodlightProvider.getSwitches().keySet();
710 // Send standard LLDPs
711 for (long sw: switches) {
712 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
713 if (iofSwitch == null) continue;
714 if (iofSwitch.getEnabledPorts() != null) {
715 for (OFPhysicalPort ofp: iofSwitch.getEnabledPorts()) {
716 if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber()))
717 continue;
718 if (autoPortFastFeature && isFastPort(sw, ofp.getPortNumber()))
719 continue;
720
721 // sends forward LLDP only non-fastports.
722 sendDiscoveryMessage(sw, ofp.getPortNumber(), true, false);
723
724 // If the switch port is not alreayd in the maintenance
725 // queue, add it.
726 NodePortTuple npt = new NodePortTuple(sw, ofp.getPortNumber());
727 addToMaintenanceQueue(npt);
728 }
729 }
730 }
731 }
732
733 protected void setControllerTLV() {
734 //Setting the controllerTLVValue based on current nano time,
735 //controller's IP address, and the network interface object hash
736 //the corresponding IP address.
737
738 final int prime = 7867;
739 InetAddress localIPAddress = null;
740 NetworkInterface localInterface = null;
741
742 byte[] controllerTLVValue = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; // 8 byte value.
743 ByteBuffer bb = ByteBuffer.allocate(10);
744
745 try{
746 localIPAddress = java.net.InetAddress.getLocalHost();
747 localInterface = NetworkInterface.getByInetAddress(localIPAddress);
748 } catch (Exception e) {
749 e.printStackTrace();
750 }
751
752 long result = System.nanoTime();
753 if (localIPAddress != null)
754 result = result * prime + IPv4.toIPv4Address(localIPAddress.getHostAddress());
755 if (localInterface != null)
756 result = result * prime + localInterface.hashCode();
757 // set the first 4 bits to 0.
758 result = result & (0x0fffffffffffffffL);
759
760 bb.putLong(result);
761
762 bb.rewind();
763 bb.get(controllerTLVValue, 0, 8);
764
765 this.controllerTLV = new LLDPTLV().setType((byte) 0x0c).setLength((short) controllerTLVValue.length).setValue(controllerTLVValue);
766 }
767
768 @Override
769 public String getName() {
770 return "linkdiscovery";
771 }
772
773 @Override
774 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
775 switch (msg.getType()) {
776 case PACKET_IN:
777 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx);
778 case PORT_STATUS:
779 return this.handlePortStatus(sw.getId(), (OFPortStatus) msg);
780 default:
781 break;
782 }
783 return Command.CONTINUE;
784 }
785
786 private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, boolean isStandard, FloodlightContext cntx) {
787 // If LLDP is suppressed on this port, ignore received packet as well
788 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
789 if (iofSwitch == null) {
790 return Command.STOP;
791 }
792
793 if (isLinkDiscoverySuppressed(sw, pi.getInPort()))
794 return Command.STOP;
795
796 // If this is a malformed LLDP, or not from us, exit
797 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3)
798 return Command.CONTINUE;
799
800 long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
801 long otherId = 0;
802 boolean myLLDP = false;
803 Boolean isReverse = null;
804
805 ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
806 portBB.position(1);
807
808 Short remotePort = portBB.getShort();
809 IOFSwitch remoteSwitch = null;
810
811 // Verify this LLDP packet matches what we're looking for
812 for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
813 if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 &&
814 lldptlv.getValue()[0] == 0x0 && lldptlv.getValue()[1] == 0x26 &&
815 lldptlv.getValue()[2] == (byte)0xe1 && lldptlv.getValue()[3] == 0x0) {
816 ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue());
817 remoteSwitch = floodlightProvider.getSwitches().get(dpidBB.getLong(4));
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800818 if (remoteSwitch == null) {
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700819 // Added by ONOS
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800820 // floodlight LLDP coming from a remote switch connected to a different controller
821 // add it to our cache of unconnected remote switches
822 remoteSwitch = addRemoteSwitch(dpidBB.getLong(4), remotePort);
823 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800824 } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8){
825 otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
826 if (myId == otherId)
827 myLLDP = true;
828 } else if (lldptlv.getType() == TLV_DIRECTION_TYPE &&
829 lldptlv.getLength() == TLV_DIRECTION_LENGTH) {
830 if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0])
831 isReverse = false;
832 else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0])
833 isReverse = true;
834 }
835 }
836
837 if (myLLDP == false) {
838 // This is not the LLDP sent by this controller.
839 // If the LLDP message has multicast bit set, then we need to broadcast
840 // the packet as a regular packet.
841 if (isStandard) {
842 if (log.isTraceEnabled()) {
843 log.trace("Getting standard LLDP from a different controller and quelching it.");
844 }
845 return Command.STOP;
846 }
847 else if (myId < otherId) {
848 if (log.isTraceEnabled()) {
849 log.trace("Getting BDDP packets from a different controller" +
850 "and letting it go through normal processing chain.");
851 }
HIGUCHI Yuta30d03302013-06-14 13:47:36 -0700852 //XXX ONOS: Fix the BDDP broadcast issue
Jonathan Hart0b2c76a2013-02-27 17:09:33 -0800853 //return Command.CONTINUE;
854 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800855 }
856 }
857
858
859 if (remoteSwitch == null) {
860 // Ignore LLDPs not generated by Floodlight, or from a switch that has recently
861 // disconnected, or from a switch connected to another Floodlight instance
862 if (log.isTraceEnabled()) {
863 log.trace("Received LLDP from remote switch not connected to the controller");
864 }
865 return Command.STOP;
866 }
867
868 if (!remoteSwitch.portEnabled(remotePort)) {
869 if (log.isTraceEnabled()) {
870 log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
871 }
872 return Command.STOP;
873 }
874 if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
875 remotePort))) {
876 if (log.isTraceEnabled()) {
877 log.trace("Ignoring link with suppressed src port: switch {} port {}",
878 remoteSwitch, remotePort);
879 }
880 return Command.STOP;
881 }
882 if (!iofSwitch.portEnabled(pi.getInPort())) {
883 if (log.isTraceEnabled()) {
884 log.trace("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort());
885 }
886 return Command.STOP;
887 }
888
889 OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
890 int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
891 physicalPort = iofSwitch.getPort(pi.getInPort());
892 int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
893
894 // Store the time of update to this link, and push it out to routingEngine
895 Link lt = new Link(remoteSwitch.getId(), remotePort, iofSwitch.getId(), pi.getInPort());
896
897
898 Long lastLldpTime = null;
899 Long lastBddpTime = null;
900
901 Long firstSeenTime = System.currentTimeMillis();
902
903 if (isStandard)
904 lastLldpTime = System.currentTimeMillis();
905 else
906 lastBddpTime = System.currentTimeMillis();
907
908 LinkInfo newLinkInfo =
909 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
910 srcPortState, dstPortState);
911
912 addOrUpdateLink(lt, newLinkInfo);
913
914 // Check if reverse link exists.
915 // If it doesn't exist and if the forward link was seen
916 // first seen within a small interval, send probe on the
917 // reverse link.
918
919 newLinkInfo = links.get(lt);
920 if (newLinkInfo != null && isStandard && isReverse == false) {
921 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
922 lt.getSrc(), lt.getSrcPort());
923 LinkInfo reverseInfo = links.get(reverseLink);
924 if (reverseInfo == null) {
925 // the reverse link does not exist.
926 if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - LINK_TIMEOUT) {
927 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), isStandard, true);
928 }
929 }
930 }
931
932 // If the received packet is a BDDP packet, then create a reverse BDDP
933 // link as well.
934 if (!isStandard) {
935 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
936 lt.getSrc(), lt.getSrcPort());
937
938 // srcPortState and dstPort state are reversed.
939 LinkInfo reverseInfo =
940 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
941 dstPortState, srcPortState);
942
943 addOrUpdateLink(reverseLink, reverseInfo);
944 }
945
946 // Remove the node ports from the quarantine and maintenance queues.
947 NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
948 NodePortTuple nptDst = new NodePortTuple(lt.getDst(), lt.getDstPort());
949 removeFromQuarantineQueue(nptSrc);
950 removeFromMaintenanceQueue(nptSrc);
951 removeFromQuarantineQueue(nptDst);
952 removeFromMaintenanceQueue(nptDst);
953
954 // Consume this message
955 return Command.STOP;
956 }
957
958 protected Command handlePacketIn(long sw, OFPacketIn pi,
959 FloodlightContext cntx) {
960 Ethernet eth =
961 IFloodlightProviderService.bcStore.get(cntx,
962 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
963
964 if(eth.getEtherType() == Ethernet.TYPE_BSN) {
965 BSN bsn = (BSN) eth.getPayload();
966 if (bsn == null) return Command.STOP;
967 if (bsn.getPayload() == null) return Command.STOP;
968 // It could be a packet other than BSN LLDP, therefore
969 // continue with the regular processing.
970 if (bsn.getPayload() instanceof LLDP == false)
971 return Command.CONTINUE;
Ubuntu9cbb4ca2013-02-07 17:19:59 +0000972 return handleLldp((LLDP) bsn.getPayload(), sw, pi, false, cntx);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800973 } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
974 return handleLldp((LLDP) eth.getPayload(), sw, pi, true, cntx);
975 } else if (eth.getEtherType() < 1500) {
976 long destMac = eth.getDestinationMAC().toLong();
977 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE){
978 if (log.isTraceEnabled()) {
979 log.trace("Ignoring packet addressed to 802.1D/Q " +
980 "reserved address.");
981 }
982 return Command.STOP;
983 }
984 }
985
986 // If packet-in is from a quarantine port, stop processing.
987 NodePortTuple npt = new NodePortTuple(sw, pi.getInPort());
988 if (quarantineQueue.contains(npt)) return Command.STOP;
989
990 return Command.CONTINUE;
991 }
992
993 protected UpdateOperation getUpdateOperation(int srcPortState,
994 int dstPortState) {
995 boolean added =
996 (((srcPortState &
997 OFPortState.OFPPS_STP_MASK.getValue()) !=
998 OFPortState.OFPPS_STP_BLOCK.getValue()) &&
999 ((dstPortState &
1000 OFPortState.OFPPS_STP_MASK.getValue()) !=
1001 OFPortState.OFPPS_STP_BLOCK.getValue()));
1002
1003 if (added) return UpdateOperation.LINK_UPDATED;
1004 return UpdateOperation.LINK_REMOVED;
1005 }
1006
1007
1008
1009 protected UpdateOperation getUpdateOperation(int srcPortState) {
1010 boolean portUp = ((srcPortState &
1011 OFPortState.OFPPS_STP_MASK.getValue()) !=
1012 OFPortState.OFPPS_STP_BLOCK.getValue());
1013
1014 if (portUp) return UpdateOperation.PORT_UP;
1015 else return UpdateOperation.PORT_DOWN;
1016 }
1017
1018 protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {
1019
1020 NodePortTuple srcNpt, dstNpt;
1021 boolean linkChanged = false;
1022
1023 lock.writeLock().lock();
1024 try {
1025 // put the new info. if an old info exists, it will be returned.
1026 LinkInfo oldInfo = links.put(lt, newInfo);
1027 if (oldInfo != null &&
1028 oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
1029 newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
1030
1031 if (log.isTraceEnabled()) {
1032 log.trace("addOrUpdateLink: {} {}",
1033 lt,
1034 (newInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast");
1035 }
1036
1037 UpdateOperation updateOperation = null;
1038 linkChanged = false;
1039
1040 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1041 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1042
1043 if (oldInfo == null) {
1044 // index it by switch source
1045 if (!switchLinks.containsKey(lt.getSrc()))
1046 switchLinks.put(lt.getSrc(), new HashSet<Link>());
1047 switchLinks.get(lt.getSrc()).add(lt);
1048
1049 // index it by switch dest
1050 if (!switchLinks.containsKey(lt.getDst()))
1051 switchLinks.put(lt.getDst(), new HashSet<Link>());
1052 switchLinks.get(lt.getDst()).add(lt);
1053
1054 // index both ends by switch:port
1055 if (!portLinks.containsKey(srcNpt))
1056 portLinks.put(srcNpt, new HashSet<Link>());
1057 portLinks.get(srcNpt).add(lt);
1058
1059 if (!portLinks.containsKey(dstNpt))
1060 portLinks.put(dstNpt, new HashSet<Link>());
1061 portLinks.get(dstNpt).add(lt);
1062
1063 // Add to portNOFLinks if the unicast valid time is null
1064 if (newInfo.getUnicastValidTime() == null)
1065 addLinkToBroadcastDomain(lt);
1066
1067 writeLinkToStorage(lt, newInfo);
Umesh Krishnaswamy2b9d5642013-01-04 11:00:27 -08001068
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001069 // ONOS: Distinguish added event separately from updated event
Pankaj Berdea41016d2013-06-10 21:18:18 -07001070 updateOperation = UpdateOperation.LINK_ADDED;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001071 linkChanged = true;
1072
1073 // Add to event history
1074 evHistTopoLink(lt.getSrc(),
1075 lt.getDst(),
1076 lt.getSrcPort(),
1077 lt.getDstPort(),
1078 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1079 getLinkType(lt, newInfo),
1080 EvAction.LINK_ADDED, "LLDP Recvd");
1081 } else {
1082 // Since the link info is already there, we need to
1083 // update the right fields.
1084 if (newInfo.getUnicastValidTime() == null) {
1085 // This is due to a multicast LLDP, so copy the old unicast
1086 // value.
1087 if (oldInfo.getUnicastValidTime() != null) {
1088 newInfo.setUnicastValidTime(oldInfo.getUnicastValidTime());
1089 }
1090 } else if (newInfo.getMulticastValidTime() == null) {
1091 // This is due to a unicast LLDP, so copy the old multicast
1092 // value.
1093 if (oldInfo.getMulticastValidTime() != null) {
1094 newInfo.setMulticastValidTime(oldInfo.getMulticastValidTime());
1095 }
1096 }
1097
1098 Long oldTime = oldInfo.getUnicastValidTime();
1099 Long newTime = newInfo.getUnicastValidTime();
1100 // the link has changed its state between openflow and non-openflow
1101 // if the unicastValidTimes are null or not null
1102 if (oldTime != null & newTime == null) {
1103 // openflow -> non-openflow transition
1104 // we need to add the link tuple to the portNOFLinks
1105 addLinkToBroadcastDomain(lt);
1106 linkChanged = true;
1107 } else if (oldTime == null & newTime != null) {
1108 // non-openflow -> openflow transition
1109 // we need to remove the link from the portNOFLinks
1110 removeLinkFromBroadcastDomain(lt);
1111 linkChanged = true;
1112 }
1113
1114 // Only update the port states if they've changed
1115 if (newInfo.getSrcPortState().intValue() !=
1116 oldInfo.getSrcPortState().intValue() ||
1117 newInfo.getDstPortState().intValue() !=
1118 oldInfo.getDstPortState().intValue())
1119 linkChanged = true;
1120
1121 // Write changes to storage. This will always write the updated
1122 // valid time, plus the port states if they've changed (i.e. if
1123 // they weren't set to null in the previous block of code.
1124 writeLinkToStorage(lt, newInfo);
HIGUCHI Yutae0515e52013-06-14 13:00:40 -07001125
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001126 if (linkChanged) {
1127 updateOperation = getUpdateOperation(newInfo.getSrcPortState(),
1128 newInfo.getDstPortState());
1129 if (log.isTraceEnabled()) {
1130 log.trace("Updated link {}", lt);
1131 }
1132 // Add to event history
1133 evHistTopoLink(lt.getSrc(),
1134 lt.getDst(),
1135 lt.getSrcPort(),
1136 lt.getDstPort(),
1137 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1138 getLinkType(lt, newInfo),
1139 EvAction.LINK_PORT_STATE_UPDATED,
1140 "LLDP Recvd");
1141 }
1142 }
1143
1144 if (linkChanged) {
1145 // find out if the link was added or removed here.
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001146 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001147 lt.getDst(), lt.getDstPort(),
1148 getLinkType(lt, newInfo),
1149 updateOperation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001150 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001151 }
1152 } finally {
1153 lock.writeLock().unlock();
1154 }
1155
1156 return linkChanged;
1157 }
1158
1159 public Map<Long, Set<Link>> getSwitchLinks() {
1160 return this.switchLinks;
1161 }
1162
1163 /**
1164 * Removes links from memory and storage.
1165 * @param links The List of @LinkTuple to delete.
1166 */
1167 protected void deleteLinks(List<Link> links, String reason) {
1168 NodePortTuple srcNpt, dstNpt;
1169
1170 lock.writeLock().lock();
1171 try {
1172 for (Link lt : links) {
1173 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1174 dstNpt =new NodePortTuple(lt.getDst(), lt.getDstPort());
1175
1176 switchLinks.get(lt.getSrc()).remove(lt);
1177 switchLinks.get(lt.getDst()).remove(lt);
1178 if (switchLinks.containsKey(lt.getSrc()) &&
1179 switchLinks.get(lt.getSrc()).isEmpty())
1180 this.switchLinks.remove(lt.getSrc());
1181 if (this.switchLinks.containsKey(lt.getDst()) &&
1182 this.switchLinks.get(lt.getDst()).isEmpty())
1183 this.switchLinks.remove(lt.getDst());
1184
1185 if (this.portLinks.get(srcNpt) != null) {
1186 this.portLinks.get(srcNpt).remove(lt);
1187 if (this.portLinks.get(srcNpt).isEmpty())
1188 this.portLinks.remove(srcNpt);
1189 }
1190 if (this.portLinks.get(dstNpt) != null) {
1191 this.portLinks.get(dstNpt).remove(lt);
1192 if (this.portLinks.get(dstNpt).isEmpty())
1193 this.portLinks.remove(dstNpt);
1194 }
1195
1196 LinkInfo info = this.links.remove(lt);
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001197 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001198 lt.getDst(), lt.getDstPort(),
1199 getLinkType(lt, info),
1200 UpdateOperation.LINK_REMOVED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001201 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001202
1203 // Update Event History
1204 evHistTopoLink(lt.getSrc(),
1205 lt.getDst(),
1206 lt.getSrcPort(),
1207 lt.getDstPort(),
1208 0, 0, // Port states
1209 ILinkDiscovery.LinkType.INVALID_LINK,
1210 EvAction.LINK_DELETED, reason);
1211
1212 // remove link from storage.
1213 removeLinkFromStorage(lt);
HIGUCHI Yutae0515e52013-06-14 13:00:40 -07001214
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001215 // TODO Whenever link is removed, it has to checked if
1216 // the switchports must be added to quarantine.
1217
1218 if (log.isTraceEnabled()) {
1219 log.trace("Deleted link {}", lt);
1220 }
1221 }
1222 } finally {
1223 lock.writeLock().unlock();
1224 }
1225 }
1226
1227 /**
1228 * Handles an OFPortStatus message from a switch. We will add or
1229 * delete LinkTupes as well re-compute the topology if needed.
1230 * @param sw The IOFSwitch that sent the port status message
1231 * @param ps The OFPortStatus message
1232 * @return The Command to continue or stop after we process this message
1233 */
1234 protected Command handlePortStatus(long sw, OFPortStatus ps) {
1235
1236 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
1237 if (iofSwitch == null) return Command.CONTINUE;
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -07001238
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001239 // ONOS: If we do not control this switch, then we should not process its port status messages
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001240 if (!registryService.hasControl(iofSwitch.getId())) return Command.CONTINUE;
1241
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001242 if (log.isTraceEnabled()) {
1243 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
1244 "config is {} state is {}",
1245 new Object[] {iofSwitch.getStringId(),
1246 ps.getDesc().getPortNumber(),
1247 ps.getReason(),
1248 ps.getDesc().getConfig(),
1249 ps.getDesc().getState()});
1250 }
1251
1252 short port = ps.getDesc().getPortNumber();
1253 NodePortTuple npt = new NodePortTuple(sw, port);
1254 boolean linkDeleted = false;
1255 boolean linkInfoChanged = false;
1256
1257 lock.writeLock().lock();
1258 try {
1259 // if ps is a delete, or a modify where the port is down or
1260 // configured down
1261 if ((byte)OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
1262 ((byte)OFPortReason.OFPPR_MODIFY.ordinal() ==
1263 ps.getReason() && !portEnabled(ps.getDesc()))) {
1264 deleteLinksOnPort(npt, "Port Status Changed");
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001265 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, UpdateOperation.PORT_DOWN));
1266 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001267 linkDeleted = true;
1268 }
1269 else if (ps.getReason() ==
1270 (byte)OFPortReason.OFPPR_MODIFY.ordinal()) {
1271 // If ps is a port modification and the port state has changed
1272 // that affects links in the topology
1273
1274 if (this.portLinks.containsKey(npt)) {
1275 for (Link lt: this.portLinks.get(npt)) {
1276 LinkInfo linkInfo = links.get(lt);
1277 assert(linkInfo != null);
1278 Integer updatedSrcPortState = null;
1279 Integer updatedDstPortState = null;
1280 if (lt.getSrc() == npt.getNodeId() &&
1281 lt.getSrcPort() == npt.getPortId() &&
1282 (linkInfo.getSrcPortState() !=
1283 ps.getDesc().getState())) {
1284 updatedSrcPortState = ps.getDesc().getState();
1285 linkInfo.setSrcPortState(updatedSrcPortState);
1286 }
1287 if (lt.getDst() == npt.getNodeId() &&
1288 lt.getDstPort() == npt.getPortId() &&
1289 (linkInfo.getDstPortState() !=
1290 ps.getDesc().getState())) {
1291 updatedDstPortState = ps.getDesc().getState();
1292 linkInfo.setDstPortState(updatedDstPortState);
1293 }
1294 if ((updatedSrcPortState != null) ||
1295 (updatedDstPortState != null)) {
1296 // The link is already known to link discovery
1297 // manager and the status has changed, therefore
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001298 // send an LinkUpdate.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001299 UpdateOperation operation =
1300 getUpdateOperation(linkInfo.getSrcPortState(),
1301 linkInfo.getDstPortState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001302 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001303 lt.getDst(), lt.getDstPort(),
1304 getLinkType(lt, linkInfo),
1305 operation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001306 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001307 writeLinkToStorage(lt, linkInfo);
1308 linkInfoChanged = true;
1309 }
1310 }
1311 }
1312
1313 UpdateOperation operation =
1314 getUpdateOperation(ps.getDesc().getState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001315 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
1316 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001317 }
1318
1319 if (!linkDeleted && !linkInfoChanged){
1320 if (log.isTraceEnabled()) {
1321 log.trace("handlePortStatus: Switch {} port #{} reason {};"+
1322 " no links to update/remove",
1323 new Object[] {HexString.toHexString(sw),
1324 ps.getDesc().getPortNumber(),
1325 ps.getReason()});
1326 }
1327 }
1328 } finally {
1329 lock.writeLock().unlock();
1330 }
1331
1332 if (!linkDeleted) {
1333 // Send LLDP right away when port state is changed for faster
1334 // cluster-merge. If it is a link delete then there is not need
1335 // to send the LLDPs right away and instead we wait for the LLDPs
1336 // to be sent on the timer as it is normally done
1337 // do it outside the write-lock
1338 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
1339 processNewPort(npt.getNodeId(), npt.getPortId());
1340 }
1341 return Command.CONTINUE;
1342 }
1343
1344 /**
1345 * Process a new port.
1346 * If link discovery is disabled on the port, then do nothing.
1347 * If autoportfast feature is enabled and the port is a fast port, then
1348 * do nothing.
1349 * Otherwise, send LLDP message. Add the port to quarantine.
1350 * @param sw
1351 * @param p
1352 */
1353 private void processNewPort(long sw, short p) {
1354 if (isLinkDiscoverySuppressed(sw, p)) {
1355 // Do nothing as link discovery is suppressed.
1356 }
1357 else if (autoPortFastFeature && isFastPort(sw, p)) {
1358 // Do nothing as the port is a fast port.
1359 }
1360 else {
1361 NodePortTuple npt = new NodePortTuple(sw, p);
1362 discover(sw, p);
1363 // if it is not a fast port, add it to quarantine.
1364 if (!isFastPort(sw, p)) {
1365 addToQuarantineQueue(npt);
1366 } else {
1367 // Add to maintenance queue to ensure that BDDP packets
1368 // are sent out.
1369 addToMaintenanceQueue(npt);
1370 }
1371 }
1372 }
1373
1374 /**
1375 * We send out LLDP messages when a switch is added to discover the topology
1376 * @param sw The IOFSwitch that connected to the controller
1377 */
1378 @Override
1379 public void addedSwitch(IOFSwitch sw) {
1380
1381 if (sw.getEnabledPorts() != null) {
1382 for (Short p : sw.getEnabledPortNumbers()) {
1383 processNewPort(sw.getId(), p);
1384 }
1385 }
1386 // Update event history
1387 evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001388 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), null,
1389 UpdateOperation.SWITCH_UPDATED));
1390 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001391 }
1392
1393 /**
1394 * When a switch disconnects we remove any links from our map and notify.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001395 */
1396 @Override
1397 public void removedSwitch(IOFSwitch iofSwitch) {
1398 // Update event history
1399 long sw = iofSwitch.getId();
1400 evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None");
1401 List<Link> eraseList = new ArrayList<Link>();
1402 lock.writeLock().lock();
1403 try {
1404 if (switchLinks.containsKey(sw)) {
1405 if (log.isTraceEnabled()) {
1406 log.trace("Handle switchRemoved. Switch {}; removing links {}",
1407 HexString.toHexString(sw), switchLinks.get(sw));
1408 }
1409 // add all tuples with an endpoint on this switch to erase list
1410 eraseList.addAll(switchLinks.get(sw));
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -07001411 deleteLinks(eraseList, "Switch Removed");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001412
1413 // Send a switch removed update
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001414 LinkUpdate update = new LinkUpdate(new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED));
1415 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001416 }
1417 } finally {
1418 lock.writeLock().unlock();
1419 }
1420 }
1421
1422 /**
1423 * We don't react the port changed notifications here. we listen for
1424 * OFPortStatus messages directly. Might consider using this notifier
1425 * instead
1426 */
1427 @Override
1428 public void switchPortChanged(Long switchId) {
1429 // no-op
1430 }
1431
1432 /**
1433 * Delete links incident on a given switch port.
1434 * @param npt
1435 * @param reason
1436 */
1437 protected void deleteLinksOnPort(NodePortTuple npt, String reason) {
1438 List<Link> eraseList = new ArrayList<Link>();
1439 if (this.portLinks.containsKey(npt)) {
1440 if (log.isTraceEnabled()) {
1441 log.trace("handlePortStatus: Switch {} port #{} " +
1442 "removing links {}",
1443 new Object[] {HexString.toHexString(npt.getNodeId()),
1444 npt.getPortId(),
1445 this.portLinks.get(npt)});
1446 }
1447 eraseList.addAll(this.portLinks.get(npt));
1448 deleteLinks(eraseList, reason);
1449 }
1450 }
1451
1452 /**
1453 * Iterates through the list of links and deletes if the
1454 * last discovery message reception time exceeds timeout values.
1455 */
1456 protected void timeoutLinks() {
1457 List<Link> eraseList = new ArrayList<Link>();
1458 Long curTime = System.currentTimeMillis();
1459 boolean linkChanged = false;
1460
1461 // reentrant required here because deleteLink also write locks
1462 lock.writeLock().lock();
1463 try {
1464 Iterator<Entry<Link, LinkInfo>> it =
1465 this.links.entrySet().iterator();
1466 while (it.hasNext()) {
1467 Entry<Link, LinkInfo> entry = it.next();
1468 Link lt = entry.getKey();
1469 LinkInfo info = entry.getValue();
1470
1471 // Timeout the unicast and multicast LLDP valid times
1472 // independently.
1473 if ((info.getUnicastValidTime() != null) &&
1474 (info.getUnicastValidTime() + (this.LINK_TIMEOUT * 1000) < curTime)){
1475 info.setUnicastValidTime(null);
1476
1477 if (info.getMulticastValidTime() != null)
1478 addLinkToBroadcastDomain(lt);
1479 // Note that even if mTime becomes null later on,
1480 // the link would be deleted, which would trigger updateClusters().
1481 linkChanged = true;
1482 }
1483 if ((info.getMulticastValidTime()!= null) &&
1484 (info.getMulticastValidTime()+ (this.LINK_TIMEOUT * 1000) < curTime)) {
1485 info.setMulticastValidTime(null);
1486 // if uTime is not null, then link will remain as openflow
1487 // link. If uTime is null, it will be deleted. So, we
1488 // don't care about linkChanged flag here.
1489 removeLinkFromBroadcastDomain(lt);
1490 linkChanged = true;
1491 }
1492 // Add to the erase list only if the unicast
1493 // time is null.
1494 if (info.getUnicastValidTime() == null &&
1495 info.getMulticastValidTime() == null){
1496 eraseList.add(entry.getKey());
1497 } else if (linkChanged) {
1498 UpdateOperation operation;
1499 operation = getUpdateOperation(info.getSrcPortState(),
1500 info.getDstPortState());
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001501 LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001502 lt.getDst(), lt.getDstPort(),
1503 getLinkType(lt, info),
1504 operation));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001505 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001506 }
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 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001792 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), SwitchType.BASIC_SWITCH,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001793 UpdateOperation.SWITCH_UPDATED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001794 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001795 }
1796 else {
1797 sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, new Boolean(true));
1798 if (log.isTraceEnabled()) {
1799 log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw);
1800 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001801 LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001802 UpdateOperation.SWITCH_UPDATED));
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001803 controller.publishUpdate(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001804 }
1805 }
1806 }
1807
1808 @Override
1809 public void rowsDeleted(String tableName, Set<Object> rowKeys) {
1810 // Ignore delete events, the switch delete will do the right thing on it's own
1811 }
1812
1813 // IFloodlightModule classes
1814
1815 @Override
1816 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
1817 Collection<Class<? extends IFloodlightService>> l =
1818 new ArrayList<Class<? extends IFloodlightService>>();
1819 l.add(ILinkDiscoveryService.class);
1820 //l.add(ITopologyService.class);
1821 return l;
1822 }
1823
1824 @Override
1825 public Map<Class<? extends IFloodlightService>, IFloodlightService>
1826 getServiceImpls() {
1827 Map<Class<? extends IFloodlightService>,
1828 IFloodlightService> m =
1829 new HashMap<Class<? extends IFloodlightService>,
1830 IFloodlightService>();
1831 // We are the class that implements the service
1832 m.put(ILinkDiscoveryService.class, this);
1833 return m;
1834 }
1835
1836 @Override
1837 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
1838 Collection<Class<? extends IFloodlightService>> l =
1839 new ArrayList<Class<? extends IFloodlightService>>();
1840 l.add(IFloodlightProviderService.class);
1841 l.add(IStorageSourceService.class);
1842 l.add(IThreadPoolService.class);
1843 l.add(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001844 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001845 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001846 return l;
1847 }
1848
1849 @Override
1850 public void init(FloodlightModuleContext context)
1851 throws FloodlightModuleException {
1852 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
1853 storageSource = context.getServiceImpl(IStorageSourceService.class);
1854 threadPool = context.getServiceImpl(IThreadPoolService.class);
1855 restApi = context.getServiceImpl(IRestApiService.class);
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001856 // Added by ONOS
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001857 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001858
1859 // Set the autoportfast feature to false.
1860 this.autoPortFastFeature = false;
1861
1862 // We create this here because there is no ordering guarantee
1863 this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
1864 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001865 this.links = new HashMap<Link, LinkInfo>();
1866 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
1867 this.suppressLinkDiscovery =
1868 Collections.synchronizedSet(new HashSet<NodePortTuple>());
1869 this.portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
1870 this.switchLinks = new HashMap<Long, Set<Link>>();
1871 this.quarantineQueue = new LinkedBlockingQueue<NodePortTuple>();
1872 this.maintenanceQueue = new LinkedBlockingQueue<NodePortTuple>();
HIGUCHI Yuta30d03302013-06-14 13:47:36 -07001873 // Added by ONOS
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -07001874 this.remoteSwitches = new HashMap<Long, IOnosRemoteSwitch>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001875
1876 this.evHistTopologySwitch =
1877 new EventHistory<EventHistoryTopologySwitch>("Topology: Switch");
1878 this.evHistTopologyLink =
1879 new EventHistory<EventHistoryTopologyLink>("Topology: Link");
1880 this.evHistTopologyCluster =
1881 new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster");
1882 }
1883
1884 @Override
1885 @LogMessageDocs({
1886 @LogMessageDoc(level="ERROR",
1887 message="No storage source found.",
1888 explanation="Storage source was not initialized; cannot initialize " +
1889 "link discovery.",
1890 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1891 @LogMessageDoc(level="ERROR",
1892 message="Error in installing listener for " +
1893 "switch config table {table}",
1894 explanation="Failed to install storage notification for the " +
1895 "switch config table",
1896 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1897 @LogMessageDoc(level="ERROR",
1898 message="No storage source found.",
1899 explanation="Storage source was not initialized; cannot initialize " +
1900 "link discovery.",
1901 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1902 @LogMessageDoc(level="ERROR",
1903 message="Exception in LLDP send timer.",
1904 explanation="An unknown error occured while sending LLDP " +
1905 "messages to switches.",
1906 recommendation=LogMessageDoc.CHECK_SWITCH)
1907 })
1908 public void startUp(FloodlightModuleContext context) {
1909 // Create our storage tables
1910 if (storageSource == null) {
1911 log.error("No storage source found.");
1912 return;
1913 }
1914
1915 storageSource.createTable(LINK_TABLE_NAME, null);
1916 storageSource.setTablePrimaryKeyName(LINK_TABLE_NAME, LINK_ID);
1917 storageSource.deleteMatchingRows(LINK_TABLE_NAME, null);
1918 // Register for storage updates for the switch table
1919 try {
1920 storageSource.addListener(SWITCH_CONFIG_TABLE_NAME, this);
1921 } catch (StorageException ex) {
1922 log.error("Error in installing listener for " +
1923 "switch table {}", SWITCH_CONFIG_TABLE_NAME);
1924 }
HIGUCHI Yutae0515e52013-06-14 13:00:40 -07001925
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001926 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Pankaj Berdedc73bb12013-08-14 13:46:38 -07001927 controller =
1928 context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001929
1930 // To be started by the first switch connection
1931 discoveryTask = new SingletonTask(ses, new Runnable() {
1932 @Override
1933 public void run() {
1934 try {
1935 discoverLinks();
1936 } catch (StorageException e) {
1937 log.error("Storage exception in LLDP send timer; " +
1938 "terminating process", e);
1939 floodlightProvider.terminate();
1940 } catch (Exception e) {
1941 log.error("Exception in LLDP send timer.", e);
1942 } finally {
1943 if (!shuttingDown) {
1944 // null role implies HA mode is not enabled.
1945 Role role = floodlightProvider.getRole();
1946 if (role == null || role == Role.MASTER) {
1947 log.trace("Rescheduling discovery task as role = {}", role);
1948 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
1949 TimeUnit.SECONDS);
1950 } else {
1951 log.trace("Stopped LLDP rescheduling due to role = {}.", role);
1952 }
1953 }
1954 }
1955 }
1956 });
1957
1958 // null role implies HA mode is not enabled.
1959 Role role = floodlightProvider.getRole();
1960 if (role == null || role == Role.MASTER) {
1961 log.trace("Setup: Rescheduling discovery task. role = {}", role);
1962 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
1963 } else {
1964 log.trace("Setup: Not scheduling LLDP as role = {}.", role);
1965 }
1966
1967 // Setup the BDDP task. It is invoked whenever switch port tuples
1968 // are added to the quarantine list.
1969 bddpTask = new SingletonTask(ses, new QuarantineWorker());
1970 bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
1971
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001972
1973 // Register for the OpenFlow messages we want to receive
1974 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1975 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1976 // Register for switch updates
1977 floodlightProvider.addOFSwitchListener(this);
1978 floodlightProvider.addHAListener(this);
1979 floodlightProvider.addInfoProvider("summary", this);
1980 if (restApi != null)
1981 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
1982 setControllerTLV();
1983 }
1984
1985 // ****************************************************
1986 // Topology Manager's Event History members and methods
1987 // ****************************************************
1988
1989 // Topology Manager event history
1990 public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch;
1991 public EventHistory<EventHistoryTopologyLink> evHistTopologyLink;
1992 public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster;
1993 public EventHistoryTopologySwitch evTopoSwitch;
1994 public EventHistoryTopologyLink evTopoLink;
1995 public EventHistoryTopologyCluster evTopoCluster;
1996
1997 // Switch Added/Deleted
1998 private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
1999 if (evTopoSwitch == null) {
2000 evTopoSwitch = new EventHistoryTopologySwitch();
2001 }
2002 evTopoSwitch.dpid = sw.getId();
2003 if ((sw.getChannel() != null) &&
2004 (SocketAddress.class.isInstance(
2005 sw.getChannel().getRemoteAddress()))) {
2006 evTopoSwitch.ipv4Addr =
2007 IPv4.toIPv4Address(((InetSocketAddress)(sw.getChannel().
2008 getRemoteAddress())).getAddress().getAddress());
2009 evTopoSwitch.l4Port =
2010 ((InetSocketAddress)(sw.getChannel().
2011 getRemoteAddress())).getPort();
2012 } else {
2013 evTopoSwitch.ipv4Addr = 0;
2014 evTopoSwitch.l4Port = 0;
2015 }
2016 evTopoSwitch.reason = reason;
2017 evTopoSwitch = evHistTopologySwitch.put(evTopoSwitch, actn);
2018 }
2019
2020 private void evHistTopoLink(long srcDpid, long dstDpid, short srcPort,
2021 short dstPort, int srcPortState, int dstPortState,
2022 ILinkDiscovery.LinkType linkType,
2023 EvAction actn, String reason) {
2024 if (evTopoLink == null) {
2025 evTopoLink = new EventHistoryTopologyLink();
2026 }
2027 evTopoLink.srcSwDpid = srcDpid;
2028 evTopoLink.dstSwDpid = dstDpid;
2029 evTopoLink.srcSwport = srcPort & 0xffff;
2030 evTopoLink.dstSwport = dstPort & 0xffff;
2031 evTopoLink.srcPortState = srcPortState;
2032 evTopoLink.dstPortState = dstPortState;
2033 evTopoLink.reason = reason;
2034 switch (linkType) {
2035 case DIRECT_LINK:
2036 evTopoLink.linkType = "DIRECT_LINK";
2037 break;
2038 case MULTIHOP_LINK:
2039 evTopoLink.linkType = "MULTIHOP_LINK";
2040 break;
2041 case TUNNEL:
2042 evTopoLink.linkType = "TUNNEL";
2043 break;
2044 case INVALID_LINK:
2045 default:
2046 evTopoLink.linkType = "Unknown";
2047 break;
2048 }
2049 evTopoLink = evHistTopologyLink.put(evTopoLink, actn);
2050 }
2051
2052 public void evHistTopoCluster(long dpid, long clusterIdOld,
2053 long clusterIdNew, EvAction action, String reason) {
2054 if (evTopoCluster == null) {
2055 evTopoCluster = new EventHistoryTopologyCluster();
2056 }
2057 evTopoCluster.dpid = dpid;
2058 evTopoCluster.clusterIdOld = clusterIdOld;
2059 evTopoCluster.clusterIdNew = clusterIdNew;
2060 evTopoCluster.reason = reason;
2061 evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action);
2062 }
2063
2064 @Override
2065 public Map<String, Object> getInfo(String type) {
2066 if (!"summary".equals(type)) return null;
2067
2068 Map<String, Object> info = new HashMap<String, Object>();
2069
2070 int num_links = 0;
2071 for (Set<Link> links : switchLinks.values())
2072 num_links += links.size();
2073 info.put("# inter-switch links", num_links / 2);
2074
2075 return info;
2076 }
2077
2078 // IHARoleListener
2079 @Override
2080 public void roleChanged(Role oldRole, Role newRole) {
2081 switch(newRole) {
2082 case MASTER:
2083 if (oldRole == Role.SLAVE) {
2084 if (log.isTraceEnabled()) {
2085 log.trace("Sending LLDPs " +
2086 "to HA change from SLAVE->MASTER");
2087 }
2088 clearAllLinks();
2089 log.debug("Role Change to Master: Rescheduling discovery task.");
2090 discoveryTask.reschedule(1, TimeUnit.MICROSECONDS);
2091 }
2092 break;
2093 case SLAVE:
2094 if (log.isTraceEnabled()) {
2095 log.trace("Clearing links due to " +
2096 "HA change to SLAVE");
2097 }
2098 switchLinks.clear();
2099 links.clear();
2100 portLinks.clear();
2101 portBroadcastDomainLinks.clear();
2102 discoverOnAllPorts();
2103 break;
2104 default:
2105 break;
2106 }
2107 }
2108
2109 @Override
2110 public void controllerNodeIPsChanged(
2111 Map<String, String> curControllerNodeIPs,
2112 Map<String, String> addedControllerNodeIPs,
2113 Map<String, String> removedControllerNodeIPs) {
2114 // ignore
2115 }
2116
2117 public boolean isAutoPortFastFeature() {
2118 return autoPortFastFeature;
2119 }
2120
2121 public void setAutoPortFastFeature(boolean autoPortFastFeature) {
2122 this.autoPortFastFeature = autoPortFastFeature;
2123 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08002124}