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