blob: 167e37a214bb47c64cd39ae69d624a6d5703ef70 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
alshabibdf652ad2014-09-09 11:53:19 -07009 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
Thomas Vachuska781d18b2014-10-27 10:31:25 -070012 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
alshabibdf652ad2014-09-09 11:53:19 -070019package org.onlab.onos.provider.of.link.impl;
20
tom9c94c5b2014-09-17 13:14:42 -070021import static org.onlab.onos.openflow.controller.Dpid.uri;
alshabibdf652ad2014-09-09 11:53:19 -070022import static org.slf4j.LoggerFactory.getLogger;
23
24import java.util.Collections;
25import java.util.HashMap;
26import java.util.HashSet;
27import java.util.Iterator;
alshabibdf652ad2014-09-09 11:53:19 -070028import java.util.Map;
29import java.util.Set;
alshabib9ee68172014-09-09 14:45:14 -070030import java.util.concurrent.ConcurrentHashMap;
alshabibdf652ad2014-09-09 11:53:19 -070031import java.util.concurrent.TimeUnit;
32import java.util.concurrent.atomic.AtomicInteger;
33
34import org.jboss.netty.util.Timeout;
35import org.jboss.netty.util.TimerTask;
36import org.onlab.onos.net.ConnectPoint;
37import org.onlab.onos.net.DeviceId;
38import org.onlab.onos.net.Link.Type;
39import org.onlab.onos.net.PortNumber;
40import org.onlab.onos.net.link.DefaultLinkDescription;
41import org.onlab.onos.net.link.LinkDescription;
42import org.onlab.onos.net.link.LinkProviderService;
tom9c94c5b2014-09-17 13:14:42 -070043import org.onlab.onos.openflow.controller.Dpid;
44import org.onlab.onos.openflow.controller.OpenFlowController;
45import org.onlab.onos.openflow.controller.OpenFlowSwitch;
alshabibdf652ad2014-09-09 11:53:19 -070046import org.onlab.packet.Ethernet;
47import org.onlab.packet.ONLabLddp;
48import org.onlab.packet.ONLabLddp.DPIDandPort;
tom8bf2e6b2014-09-10 20:53:54 -070049import org.onlab.util.Timer;
alshabibdf652ad2014-09-09 11:53:19 -070050import org.projectfloodlight.openflow.protocol.OFFactory;
51import org.projectfloodlight.openflow.protocol.OFMessage;
52import org.projectfloodlight.openflow.protocol.OFPacketOut;
53import org.projectfloodlight.openflow.protocol.OFPortDesc;
54import org.projectfloodlight.openflow.protocol.action.OFAction;
55import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
56import org.projectfloodlight.openflow.types.OFBufferId;
57import org.projectfloodlight.openflow.types.OFPort;
58import org.slf4j.Logger;
59
60
61
62/**
63 * Run discovery process from a physical switch. Ports are initially labeled as
64 * slow ports. When an LLDP is successfully received, label the remote port as
65 * fast. Every probeRate milliseconds, loop over all fast ports and send an
66 * LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
67 * discovery implementation.
68 *
69 * TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen
70 * for flow_removed messages
71 */
alshabib75e77422014-10-16 18:03:40 -070072@Deprecated
alshabibdf652ad2014-09-09 11:53:19 -070073public class LinkDiscovery implements TimerTask {
74
75 private final OpenFlowSwitch sw;
76 // send 1 probe every probeRate milliseconds
77 private final long probeRate;
78 private final Set<Integer> slowPorts;
79 private final Set<Integer> fastPorts;
80 // number of unacknowledged probes per port
81 private final Map<Integer, AtomicInteger> portProbeCount;
82 // number of probes to send before link is removed
83 private static final short MAX_PROBE_COUNT = 3;
84 private Iterator<Integer> slowIterator;
85 private final OFFactory ofFactory;
86 private final Logger log = getLogger(getClass());
87 private final ONLabLddp lldpPacket;
88 private final Ethernet ethPacket;
89 private Ethernet bddpEth;
90 private final boolean useBDDP;
91 private final OpenFlowController ctrl;
92 private final LinkProviderService linkProvider;
alshabib64231d82014-09-25 18:25:31 -070093 protected final Map<Integer, OFPortDesc> ports;
alshabibc944fd02014-09-10 17:55:17 -070094 private Timeout timeout;
alshabibdf652ad2014-09-09 11:53:19 -070095
96 /**
97 * Instantiates discovery manager for the given physical switch. Creates a
98 * generic LLDP packet that will be customized for the port it is sent out on.
99 * Starts the the timer for the discovery process.
100 *
101 * @param sw the physical switch
102 * @param useBDDP flag to also use BDDP for discovery
103 */
104 public LinkDiscovery(final OpenFlowSwitch sw,
105 OpenFlowController ctrl, LinkProviderService providerService, Boolean... useBDDP) {
106 this.sw = sw;
107 this.ofFactory = sw.factory();
108 this.ctrl = ctrl;
alshabib7674db42014-09-12 23:40:46 -0700109 this.probeRate = 3000;
alshabibdf652ad2014-09-09 11:53:19 -0700110 this.linkProvider = providerService;
111 this.slowPorts = Collections.synchronizedSet(new HashSet<Integer>());
112 this.fastPorts = Collections.synchronizedSet(new HashSet<Integer>());
alshabib9ee68172014-09-09 14:45:14 -0700113 this.ports = new ConcurrentHashMap<>();
alshabibdf652ad2014-09-09 11:53:19 -0700114 this.portProbeCount = new HashMap<Integer, AtomicInteger>();
115 this.lldpPacket = new ONLabLddp();
116 this.lldpPacket.setSwitch(this.sw.getId());
117 this.ethPacket = new Ethernet();
118 this.ethPacket.setEtherType(Ethernet.TYPE_LLDP);
119 this.ethPacket.setDestinationMACAddress(ONLabLddp.LLDP_NICIRA);
120 this.ethPacket.setPayload(this.lldpPacket);
121 this.ethPacket.setPad(true);
122 this.useBDDP = useBDDP.length > 0 ? useBDDP[0] : false;
123 if (this.useBDDP) {
124 this.bddpEth = new Ethernet();
125 this.bddpEth.setPayload(this.lldpPacket);
126 this.bddpEth.setEtherType(Ethernet.TYPE_BSN);
127 this.bddpEth.setDestinationMACAddress(ONLabLddp.BDDP_MULTICAST);
128 this.bddpEth.setPad(true);
129 log.info("Using BDDP to discover network");
130 }
131 for (OFPortDesc port : sw.getPorts()) {
132 if (port.getPortNo() != OFPort.LOCAL) {
133 addPort(port);
134 }
135 }
alshabib7674db42014-09-12 23:40:46 -0700136 timeout = Timer.getTimer().newTimeout(this, 0,
alshabibdf652ad2014-09-09 11:53:19 -0700137 TimeUnit.MILLISECONDS);
alshabib6eb438a2014-10-01 16:39:37 -0700138 this.log.info("Started discovery manager for switch {}",
alshabibdf652ad2014-09-09 11:53:19 -0700139 sw.getId());
140
141 }
142
143 /**
144 * Add physical port port to discovery process.
145 * Send out initial LLDP and label it as slow port.
146 *
147 * @param port the port
148 */
149 public void addPort(final OFPortDesc port) {
150 // Ignore ports that are not on this switch, or already booted. */
alshabib9ee68172014-09-09 14:45:14 -0700151 this.ports.put(port.getPortNo().getPortNumber(), port);
alshabibdf652ad2014-09-09 11:53:19 -0700152 synchronized (this) {
153 this.log.debug("sending init probe to port {}",
154 port.getPortNo().getPortNumber());
155 OFPacketOut pkt;
156
157 pkt = this.createLLDPPacketOut(port);
158 this.sw.sendMsg(pkt);
159 if (useBDDP) {
160 OFPacketOut bpkt = this.createBDDPPacketOut(port);
161 this.sw.sendMsg(bpkt);
162 }
163
164 this.slowPorts.add(port.getPortNo().getPortNumber());
alshabibdf652ad2014-09-09 11:53:19 -0700165 }
166
167 }
168
169 /**
170 * Removes physical port from discovery process.
171 *
172 * @param port the port
173 */
alshabiba159a322014-09-09 14:50:51 -0700174 public void removePort(final OFPortDesc port) {
alshabibdf652ad2014-09-09 11:53:19 -0700175 // Ignore ports that are not on this switch
176
alshabiba159a322014-09-09 14:50:51 -0700177 int portnum = port.getPortNo().getPortNumber();
178 this.ports.remove(portnum);
alshabibdf652ad2014-09-09 11:53:19 -0700179 synchronized (this) {
180 if (this.slowPorts.contains(portnum)) {
181 this.slowPorts.remove(portnum);
alshabibdf652ad2014-09-09 11:53:19 -0700182
183 } else if (this.fastPorts.contains(portnum)) {
184 this.fastPorts.remove(portnum);
185 this.portProbeCount.remove(portnum);
186 // no iterator to update
187 } else {
188 this.log.warn(
189 "tried to dynamically remove non-existing port {}",
190 portnum);
191 }
192 }
alshabibc944fd02014-09-10 17:55:17 -0700193 ConnectPoint cp = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700194 DeviceId.deviceId(uri(sw.getId())),
alshabibc944fd02014-09-10 17:55:17 -0700195 PortNumber.portNumber(port.getPortNo().getPortNumber()));
196 linkProvider.linksVanished(cp);
alshabibdf652ad2014-09-09 11:53:19 -0700197
198 }
199
200 /**
201 * Method called by remote port to acknowledge receipt of LLDP sent by
202 * this port. If slow port, updates label to fast. If fast port, decrements
203 * number of unacknowledged probes.
204 *
205 * @param port the port
206 */
207 public void ackProbe(final Integer port) {
208 final int portNumber = port;
209 synchronized (this) {
210 if (this.slowPorts.contains(portNumber)) {
211 this.log.debug("Setting slow port to fast: {}:{}",
212 this.sw.getId(), portNumber);
213 this.slowPorts.remove(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700214 this.fastPorts.add(portNumber);
215 this.portProbeCount.put(portNumber, new AtomicInteger(0));
216 } else {
217 if (this.fastPorts.contains(portNumber)) {
218 this.portProbeCount.get(portNumber).set(0);
219 } else {
220 this.log.debug(
221 "Got ackProbe for non-existing port: {}",
222 portNumber);
223 }
224 }
225 }
226 }
227
228 /**
229 * Creates packet_out LLDP for specified output port.
230 *
231 * @param port the port
232 * @return Packet_out message with LLDP data
alshabibdf652ad2014-09-09 11:53:19 -0700233 */
234 private OFPacketOut createLLDPPacketOut(final OFPortDesc port) {
alshabib89e43ef2014-09-16 10:36:34 -0700235 if (port == null) {
236 return null;
237 }
alshabibdf652ad2014-09-09 11:53:19 -0700238 OFPacketOut.Builder packetOut = this.ofFactory.buildPacketOut();
239 packetOut.setBufferId(OFBufferId.NO_BUFFER);
240 OFAction act = this.ofFactory.actions().buildOutput()
241 .setPort(port.getPortNo()).build();
242 packetOut.setActions(Collections.singletonList(act));
243 this.lldpPacket.setPort(port.getPortNo().getPortNumber());
244 this.ethPacket.setSourceMACAddress(port.getHwAddr().getBytes());
245
246 final byte[] lldp = this.ethPacket.serialize();
247 packetOut.setData(lldp);
248 return packetOut.build();
249 }
250
251 /**
252 * Creates packet_out BDDP for specified output port.
253 *
254 * @param port the port
255 * @return Packet_out message with LLDP data
alshabibdf652ad2014-09-09 11:53:19 -0700256 */
257 private OFPacketOut createBDDPPacketOut(final OFPortDesc port) {
alshabib89e43ef2014-09-16 10:36:34 -0700258 if (port == null) {
259 return null;
260 }
alshabibdf652ad2014-09-09 11:53:19 -0700261 OFPacketOut.Builder packetOut = sw.factory().buildPacketOut();
262
263 packetOut.setBufferId(OFBufferId.NO_BUFFER);
264
265 OFActionOutput.Builder act = sw.factory().actions().buildOutput()
266 .setPort(port.getPortNo());
267 OFAction out = act.build();
268 packetOut.setActions(Collections.singletonList(out));
269 this.lldpPacket.setPort(port.getPortNo().getPortNumber());
270 this.bddpEth.setSourceMACAddress(port.getHwAddr().getBytes());
271
272 final byte[] bddp = this.bddpEth.serialize();
273 packetOut.setData(bddp);
274
275 return packetOut.build();
276 }
277
278
279 private void sendMsg(final OFMessage msg) {
alshabib89e43ef2014-09-16 10:36:34 -0700280 if (msg == null) {
281 return;
282 }
alshabibdf652ad2014-09-09 11:53:19 -0700283 this.sw.sendMsg(msg);
284 }
285
286 public String getName() {
287 return "LinkDiscovery " + this.sw.getStringId();
288 }
289
alshabib818d57c2014-09-15 22:34:30 -0700290 /**
alshabibdf652ad2014-09-09 11:53:19 -0700291 * Handles an incoming LLDP packet. Creates link in topology and sends ACK
292 * to port where LLDP originated.
293 */
alshabib505bc6b2014-09-09 15:04:13 -0700294 public boolean handleLLDP(final byte[] pkt, Integer inPort) {
alshabibdf652ad2014-09-09 11:53:19 -0700295
alshabib9ee68172014-09-09 14:45:14 -0700296 short ethType = ONLabLddp.isOVXLLDP(pkt);
297 if (ethType == Ethernet.TYPE_LLDP || ethType == Ethernet.TYPE_BSN) {
alshabibdf652ad2014-09-09 11:53:19 -0700298 final Integer dstPort = inPort;
299 final DPIDandPort dp = ONLabLddp.parseLLDP(pkt);
300 final OpenFlowSwitch srcSwitch = ctrl.getSwitch(new Dpid(dp.getDpid()));
301 final Integer srcPort = dp.getPort();
302 if (srcSwitch == null) {
alshabib505bc6b2014-09-09 15:04:13 -0700303 return true;
alshabibdf652ad2014-09-09 11:53:19 -0700304 }
305 this.ackProbe(srcPort);
306 ConnectPoint src = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700307 DeviceId.deviceId(uri(srcSwitch.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700308 PortNumber.portNumber(srcPort));
309
310 ConnectPoint dst = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700311 DeviceId.deviceId(uri(sw.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700312 PortNumber.portNumber(dstPort));
313 LinkDescription ld;
alshabib9ee68172014-09-09 14:45:14 -0700314 if (ethType == Ethernet.TYPE_BSN) {
alshabibdf652ad2014-09-09 11:53:19 -0700315 ld = new DefaultLinkDescription(src, dst, Type.INDIRECT);
316 } else {
317 ld = new DefaultLinkDescription(src, dst, Type.DIRECT);
318 }
319 linkProvider.linkDetected(ld);
alshabib505bc6b2014-09-09 15:04:13 -0700320 return true;
alshabibdf652ad2014-09-09 11:53:19 -0700321 } else {
322 this.log.debug("Ignoring unknown LLDP");
alshabib505bc6b2014-09-09 15:04:13 -0700323 return false;
alshabibdf652ad2014-09-09 11:53:19 -0700324 }
325 }
326
alshabib9ee68172014-09-09 14:45:14 -0700327 private OFPortDesc findPort(Integer inPort) {
328 return ports.get(inPort);
alshabibdf652ad2014-09-09 11:53:19 -0700329 }
330
331 /**
332 * Execute this method every t milliseconds. Loops over all ports
333 * labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
334 * port.
335 *
336 * @param t timeout
337 * @throws Exception
338 */
339 @Override
340 public void run(final Timeout t) {
341 this.log.debug("sending probes");
342 synchronized (this) {
343 final Iterator<Integer> fastIterator = this.fastPorts.iterator();
344 while (fastIterator.hasNext()) {
345 final Integer portNumber = fastIterator.next();
Yuta HIGUCHI70ce8432014-10-15 23:59:10 -0700346 OFPortDesc port = findPort(portNumber);
347 if (port == null) {
348 // port can be null
349 // #removePort modifies `ports` outside synchronized block
350 continue;
351 }
alshabibdf652ad2014-09-09 11:53:19 -0700352 final int probeCount = this.portProbeCount.get(portNumber)
353 .getAndIncrement();
alshabibdf652ad2014-09-09 11:53:19 -0700354 if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
355 this.log.debug("sending fast probe to port");
356
357 OFPacketOut pkt = this.createLLDPPacketOut(port);
358 this.sendMsg(pkt);
359 if (useBDDP) {
360 OFPacketOut bpkt = this.createBDDPPacketOut(port);
361 this.sendMsg(bpkt);
362 }
363 } else {
364 // Update fast and slow ports
365 fastIterator.remove();
366 this.slowPorts.add(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700367 this.portProbeCount.remove(portNumber);
368
369 // Remove link from topology
370 final OFPortDesc srcPort = port;
371
372 ConnectPoint cp = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700373 DeviceId.deviceId(uri(sw.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700374 PortNumber.portNumber(srcPort.getPortNo().getPortNumber()));
375 linkProvider.linksVanished(cp);
376 }
377 }
378
379 // send a probe for the next slow port
alshabib818d57c2014-09-15 22:34:30 -0700380 if (!this.slowPorts.isEmpty()) {
381 this.slowIterator = this.slowPorts.iterator();
382 while (this.slowIterator.hasNext()) {
alshabibdf652ad2014-09-09 11:53:19 -0700383 final int portNumber = this.slowIterator.next();
384 this.log.debug("sending slow probe to port {}", portNumber);
alshabib9ee68172014-09-09 14:45:14 -0700385 OFPortDesc port = findPort(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700386
387 OFPacketOut pkt = this.createLLDPPacketOut(port);
388 this.sendMsg(pkt);
389 if (useBDDP) {
390 OFPacketOut bpkt = this.createBDDPPacketOut(port);
391 this.sendMsg(bpkt);
392 }
393
394 }
395 }
396 }
397
398 // reschedule timer
alshabibc944fd02014-09-10 17:55:17 -0700399 timeout = Timer.getTimer().newTimeout(this, this.probeRate,
alshabibdf652ad2014-09-09 11:53:19 -0700400 TimeUnit.MILLISECONDS);
401 }
402
403 public void removeAllPorts() {
alshabibc944fd02014-09-10 17:55:17 -0700404 for (OFPortDesc port : ports.values()) {
alshabiba159a322014-09-09 14:50:51 -0700405 removePort(port);
alshabibdf652ad2014-09-09 11:53:19 -0700406 }
407 }
408
alshabibc944fd02014-09-10 17:55:17 -0700409 public void stop() {
alshabibc944fd02014-09-10 17:55:17 -0700410 timeout.cancel();
alshabib89e43ef2014-09-16 10:36:34 -0700411 removeAllPorts();
alshabibc944fd02014-09-10 17:55:17 -0700412 }
413
alshabibdf652ad2014-09-09 11:53:19 -0700414}