blob: 0c84aeed5e24a25c87e02f2f28234241554659d8 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
alshabibdf652ad2014-09-09 11:53:19 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
alshabibdf652ad2014-09-09 11:53:19 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
alshabibdf652ad2014-09-09 11:53:19 -070016package org.onlab.onos.provider.of.link.impl;
17
tom9c94c5b2014-09-17 13:14:42 -070018import static org.onlab.onos.openflow.controller.Dpid.uri;
alshabibdf652ad2014-09-09 11:53:19 -070019import static org.slf4j.LoggerFactory.getLogger;
20
21import java.util.Collections;
22import java.util.HashMap;
23import java.util.HashSet;
24import java.util.Iterator;
alshabibdf652ad2014-09-09 11:53:19 -070025import java.util.Map;
26import java.util.Set;
alshabib9ee68172014-09-09 14:45:14 -070027import java.util.concurrent.ConcurrentHashMap;
alshabibdf652ad2014-09-09 11:53:19 -070028import java.util.concurrent.TimeUnit;
29import java.util.concurrent.atomic.AtomicInteger;
30
31import org.jboss.netty.util.Timeout;
32import org.jboss.netty.util.TimerTask;
33import org.onlab.onos.net.ConnectPoint;
34import org.onlab.onos.net.DeviceId;
35import org.onlab.onos.net.Link.Type;
36import org.onlab.onos.net.PortNumber;
37import org.onlab.onos.net.link.DefaultLinkDescription;
38import org.onlab.onos.net.link.LinkDescription;
39import org.onlab.onos.net.link.LinkProviderService;
tom9c94c5b2014-09-17 13:14:42 -070040import org.onlab.onos.openflow.controller.Dpid;
41import org.onlab.onos.openflow.controller.OpenFlowController;
42import org.onlab.onos.openflow.controller.OpenFlowSwitch;
alshabibdf652ad2014-09-09 11:53:19 -070043import org.onlab.packet.Ethernet;
44import org.onlab.packet.ONLabLddp;
45import org.onlab.packet.ONLabLddp.DPIDandPort;
tom8bf2e6b2014-09-10 20:53:54 -070046import org.onlab.util.Timer;
alshabibdf652ad2014-09-09 11:53:19 -070047import org.projectfloodlight.openflow.protocol.OFFactory;
48import org.projectfloodlight.openflow.protocol.OFMessage;
49import org.projectfloodlight.openflow.protocol.OFPacketOut;
50import org.projectfloodlight.openflow.protocol.OFPortDesc;
51import org.projectfloodlight.openflow.protocol.action.OFAction;
52import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
53import org.projectfloodlight.openflow.types.OFBufferId;
54import org.projectfloodlight.openflow.types.OFPort;
55import org.slf4j.Logger;
56
57
58
59/**
60 * Run discovery process from a physical switch. Ports are initially labeled as
61 * slow ports. When an LLDP is successfully received, label the remote port as
62 * fast. Every probeRate milliseconds, loop over all fast ports and send an
63 * LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
64 * discovery implementation.
65 *
66 * TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen
67 * for flow_removed messages
68 */
alshabib75e77422014-10-16 18:03:40 -070069@Deprecated
alshabibdf652ad2014-09-09 11:53:19 -070070public class LinkDiscovery implements TimerTask {
71
72 private final OpenFlowSwitch sw;
73 // send 1 probe every probeRate milliseconds
74 private final long probeRate;
75 private final Set<Integer> slowPorts;
76 private final Set<Integer> fastPorts;
77 // number of unacknowledged probes per port
78 private final Map<Integer, AtomicInteger> portProbeCount;
79 // number of probes to send before link is removed
80 private static final short MAX_PROBE_COUNT = 3;
81 private Iterator<Integer> slowIterator;
82 private final OFFactory ofFactory;
83 private final Logger log = getLogger(getClass());
84 private final ONLabLddp lldpPacket;
85 private final Ethernet ethPacket;
86 private Ethernet bddpEth;
87 private final boolean useBDDP;
88 private final OpenFlowController ctrl;
89 private final LinkProviderService linkProvider;
alshabib64231d82014-09-25 18:25:31 -070090 protected final Map<Integer, OFPortDesc> ports;
alshabibc944fd02014-09-10 17:55:17 -070091 private Timeout timeout;
alshabibdf652ad2014-09-09 11:53:19 -070092
93 /**
94 * Instantiates discovery manager for the given physical switch. Creates a
95 * generic LLDP packet that will be customized for the port it is sent out on.
96 * Starts the the timer for the discovery process.
97 *
98 * @param sw the physical switch
99 * @param useBDDP flag to also use BDDP for discovery
100 */
101 public LinkDiscovery(final OpenFlowSwitch sw,
102 OpenFlowController ctrl, LinkProviderService providerService, Boolean... useBDDP) {
103 this.sw = sw;
104 this.ofFactory = sw.factory();
105 this.ctrl = ctrl;
alshabib7674db42014-09-12 23:40:46 -0700106 this.probeRate = 3000;
alshabibdf652ad2014-09-09 11:53:19 -0700107 this.linkProvider = providerService;
108 this.slowPorts = Collections.synchronizedSet(new HashSet<Integer>());
109 this.fastPorts = Collections.synchronizedSet(new HashSet<Integer>());
alshabib9ee68172014-09-09 14:45:14 -0700110 this.ports = new ConcurrentHashMap<>();
alshabibdf652ad2014-09-09 11:53:19 -0700111 this.portProbeCount = new HashMap<Integer, AtomicInteger>();
112 this.lldpPacket = new ONLabLddp();
113 this.lldpPacket.setSwitch(this.sw.getId());
114 this.ethPacket = new Ethernet();
115 this.ethPacket.setEtherType(Ethernet.TYPE_LLDP);
116 this.ethPacket.setDestinationMACAddress(ONLabLddp.LLDP_NICIRA);
117 this.ethPacket.setPayload(this.lldpPacket);
118 this.ethPacket.setPad(true);
119 this.useBDDP = useBDDP.length > 0 ? useBDDP[0] : false;
120 if (this.useBDDP) {
121 this.bddpEth = new Ethernet();
122 this.bddpEth.setPayload(this.lldpPacket);
123 this.bddpEth.setEtherType(Ethernet.TYPE_BSN);
124 this.bddpEth.setDestinationMACAddress(ONLabLddp.BDDP_MULTICAST);
125 this.bddpEth.setPad(true);
126 log.info("Using BDDP to discover network");
127 }
128 for (OFPortDesc port : sw.getPorts()) {
129 if (port.getPortNo() != OFPort.LOCAL) {
130 addPort(port);
131 }
132 }
alshabib7674db42014-09-12 23:40:46 -0700133 timeout = Timer.getTimer().newTimeout(this, 0,
alshabibdf652ad2014-09-09 11:53:19 -0700134 TimeUnit.MILLISECONDS);
alshabib6eb438a2014-10-01 16:39:37 -0700135 this.log.info("Started discovery manager for switch {}",
alshabibdf652ad2014-09-09 11:53:19 -0700136 sw.getId());
137
138 }
139
140 /**
141 * Add physical port port to discovery process.
142 * Send out initial LLDP and label it as slow port.
143 *
144 * @param port the port
145 */
146 public void addPort(final OFPortDesc port) {
147 // Ignore ports that are not on this switch, or already booted. */
alshabib9ee68172014-09-09 14:45:14 -0700148 this.ports.put(port.getPortNo().getPortNumber(), port);
alshabibdf652ad2014-09-09 11:53:19 -0700149 synchronized (this) {
150 this.log.debug("sending init probe to port {}",
151 port.getPortNo().getPortNumber());
152 OFPacketOut pkt;
153
154 pkt = this.createLLDPPacketOut(port);
155 this.sw.sendMsg(pkt);
156 if (useBDDP) {
157 OFPacketOut bpkt = this.createBDDPPacketOut(port);
158 this.sw.sendMsg(bpkt);
159 }
160
161 this.slowPorts.add(port.getPortNo().getPortNumber());
alshabibdf652ad2014-09-09 11:53:19 -0700162 }
163
164 }
165
166 /**
167 * Removes physical port from discovery process.
168 *
169 * @param port the port
170 */
alshabiba159a322014-09-09 14:50:51 -0700171 public void removePort(final OFPortDesc port) {
alshabibdf652ad2014-09-09 11:53:19 -0700172 // Ignore ports that are not on this switch
173
alshabiba159a322014-09-09 14:50:51 -0700174 int portnum = port.getPortNo().getPortNumber();
175 this.ports.remove(portnum);
alshabibdf652ad2014-09-09 11:53:19 -0700176 synchronized (this) {
177 if (this.slowPorts.contains(portnum)) {
178 this.slowPorts.remove(portnum);
alshabibdf652ad2014-09-09 11:53:19 -0700179
180 } else if (this.fastPorts.contains(portnum)) {
181 this.fastPorts.remove(portnum);
182 this.portProbeCount.remove(portnum);
183 // no iterator to update
184 } else {
185 this.log.warn(
186 "tried to dynamically remove non-existing port {}",
187 portnum);
188 }
189 }
alshabibc944fd02014-09-10 17:55:17 -0700190 ConnectPoint cp = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700191 DeviceId.deviceId(uri(sw.getId())),
alshabibc944fd02014-09-10 17:55:17 -0700192 PortNumber.portNumber(port.getPortNo().getPortNumber()));
193 linkProvider.linksVanished(cp);
alshabibdf652ad2014-09-09 11:53:19 -0700194
195 }
196
197 /**
198 * Method called by remote port to acknowledge receipt of LLDP sent by
199 * this port. If slow port, updates label to fast. If fast port, decrements
200 * number of unacknowledged probes.
201 *
202 * @param port the port
203 */
204 public void ackProbe(final Integer port) {
205 final int portNumber = port;
206 synchronized (this) {
207 if (this.slowPorts.contains(portNumber)) {
208 this.log.debug("Setting slow port to fast: {}:{}",
209 this.sw.getId(), portNumber);
210 this.slowPorts.remove(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700211 this.fastPorts.add(portNumber);
212 this.portProbeCount.put(portNumber, new AtomicInteger(0));
213 } else {
214 if (this.fastPorts.contains(portNumber)) {
215 this.portProbeCount.get(portNumber).set(0);
216 } else {
217 this.log.debug(
218 "Got ackProbe for non-existing port: {}",
219 portNumber);
220 }
221 }
222 }
223 }
224
225 /**
226 * Creates packet_out LLDP for specified output port.
227 *
228 * @param port the port
229 * @return Packet_out message with LLDP data
alshabibdf652ad2014-09-09 11:53:19 -0700230 */
231 private OFPacketOut createLLDPPacketOut(final OFPortDesc port) {
alshabib89e43ef2014-09-16 10:36:34 -0700232 if (port == null) {
233 return null;
234 }
alshabibdf652ad2014-09-09 11:53:19 -0700235 OFPacketOut.Builder packetOut = this.ofFactory.buildPacketOut();
236 packetOut.setBufferId(OFBufferId.NO_BUFFER);
237 OFAction act = this.ofFactory.actions().buildOutput()
238 .setPort(port.getPortNo()).build();
239 packetOut.setActions(Collections.singletonList(act));
240 this.lldpPacket.setPort(port.getPortNo().getPortNumber());
241 this.ethPacket.setSourceMACAddress(port.getHwAddr().getBytes());
242
243 final byte[] lldp = this.ethPacket.serialize();
244 packetOut.setData(lldp);
245 return packetOut.build();
246 }
247
248 /**
249 * Creates packet_out BDDP for specified output port.
250 *
251 * @param port the port
252 * @return Packet_out message with LLDP data
alshabibdf652ad2014-09-09 11:53:19 -0700253 */
254 private OFPacketOut createBDDPPacketOut(final OFPortDesc port) {
alshabib89e43ef2014-09-16 10:36:34 -0700255 if (port == null) {
256 return null;
257 }
alshabibdf652ad2014-09-09 11:53:19 -0700258 OFPacketOut.Builder packetOut = sw.factory().buildPacketOut();
259
260 packetOut.setBufferId(OFBufferId.NO_BUFFER);
261
262 OFActionOutput.Builder act = sw.factory().actions().buildOutput()
263 .setPort(port.getPortNo());
264 OFAction out = act.build();
265 packetOut.setActions(Collections.singletonList(out));
266 this.lldpPacket.setPort(port.getPortNo().getPortNumber());
267 this.bddpEth.setSourceMACAddress(port.getHwAddr().getBytes());
268
269 final byte[] bddp = this.bddpEth.serialize();
270 packetOut.setData(bddp);
271
272 return packetOut.build();
273 }
274
275
276 private void sendMsg(final OFMessage msg) {
alshabib89e43ef2014-09-16 10:36:34 -0700277 if (msg == null) {
278 return;
279 }
alshabibdf652ad2014-09-09 11:53:19 -0700280 this.sw.sendMsg(msg);
281 }
282
283 public String getName() {
284 return "LinkDiscovery " + this.sw.getStringId();
285 }
286
alshabib818d57c2014-09-15 22:34:30 -0700287 /**
alshabibdf652ad2014-09-09 11:53:19 -0700288 * Handles an incoming LLDP packet. Creates link in topology and sends ACK
289 * to port where LLDP originated.
290 */
alshabib505bc6b2014-09-09 15:04:13 -0700291 public boolean handleLLDP(final byte[] pkt, Integer inPort) {
alshabibdf652ad2014-09-09 11:53:19 -0700292
alshabib9ee68172014-09-09 14:45:14 -0700293 short ethType = ONLabLddp.isOVXLLDP(pkt);
294 if (ethType == Ethernet.TYPE_LLDP || ethType == Ethernet.TYPE_BSN) {
alshabibdf652ad2014-09-09 11:53:19 -0700295 final Integer dstPort = inPort;
296 final DPIDandPort dp = ONLabLddp.parseLLDP(pkt);
297 final OpenFlowSwitch srcSwitch = ctrl.getSwitch(new Dpid(dp.getDpid()));
298 final Integer srcPort = dp.getPort();
299 if (srcSwitch == null) {
alshabib505bc6b2014-09-09 15:04:13 -0700300 return true;
alshabibdf652ad2014-09-09 11:53:19 -0700301 }
302 this.ackProbe(srcPort);
303 ConnectPoint src = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700304 DeviceId.deviceId(uri(srcSwitch.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700305 PortNumber.portNumber(srcPort));
306
307 ConnectPoint dst = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700308 DeviceId.deviceId(uri(sw.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700309 PortNumber.portNumber(dstPort));
310 LinkDescription ld;
alshabib9ee68172014-09-09 14:45:14 -0700311 if (ethType == Ethernet.TYPE_BSN) {
alshabibdf652ad2014-09-09 11:53:19 -0700312 ld = new DefaultLinkDescription(src, dst, Type.INDIRECT);
313 } else {
314 ld = new DefaultLinkDescription(src, dst, Type.DIRECT);
315 }
316 linkProvider.linkDetected(ld);
alshabib505bc6b2014-09-09 15:04:13 -0700317 return true;
alshabibdf652ad2014-09-09 11:53:19 -0700318 } else {
319 this.log.debug("Ignoring unknown LLDP");
alshabib505bc6b2014-09-09 15:04:13 -0700320 return false;
alshabibdf652ad2014-09-09 11:53:19 -0700321 }
322 }
323
alshabib9ee68172014-09-09 14:45:14 -0700324 private OFPortDesc findPort(Integer inPort) {
325 return ports.get(inPort);
alshabibdf652ad2014-09-09 11:53:19 -0700326 }
327
328 /**
329 * Execute this method every t milliseconds. Loops over all ports
330 * labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
331 * port.
332 *
333 * @param t timeout
334 * @throws Exception
335 */
336 @Override
337 public void run(final Timeout t) {
338 this.log.debug("sending probes");
339 synchronized (this) {
340 final Iterator<Integer> fastIterator = this.fastPorts.iterator();
341 while (fastIterator.hasNext()) {
342 final Integer portNumber = fastIterator.next();
Yuta HIGUCHI70ce8432014-10-15 23:59:10 -0700343 OFPortDesc port = findPort(portNumber);
344 if (port == null) {
345 // port can be null
346 // #removePort modifies `ports` outside synchronized block
347 continue;
348 }
alshabibdf652ad2014-09-09 11:53:19 -0700349 final int probeCount = this.portProbeCount.get(portNumber)
350 .getAndIncrement();
alshabibdf652ad2014-09-09 11:53:19 -0700351 if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
352 this.log.debug("sending fast probe to port");
353
354 OFPacketOut pkt = this.createLLDPPacketOut(port);
355 this.sendMsg(pkt);
356 if (useBDDP) {
357 OFPacketOut bpkt = this.createBDDPPacketOut(port);
358 this.sendMsg(bpkt);
359 }
360 } else {
361 // Update fast and slow ports
362 fastIterator.remove();
363 this.slowPorts.add(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700364 this.portProbeCount.remove(portNumber);
365
366 // Remove link from topology
367 final OFPortDesc srcPort = port;
368
369 ConnectPoint cp = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700370 DeviceId.deviceId(uri(sw.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700371 PortNumber.portNumber(srcPort.getPortNo().getPortNumber()));
372 linkProvider.linksVanished(cp);
373 }
374 }
375
376 // send a probe for the next slow port
alshabib818d57c2014-09-15 22:34:30 -0700377 if (!this.slowPorts.isEmpty()) {
378 this.slowIterator = this.slowPorts.iterator();
379 while (this.slowIterator.hasNext()) {
alshabibdf652ad2014-09-09 11:53:19 -0700380 final int portNumber = this.slowIterator.next();
381 this.log.debug("sending slow probe to port {}", portNumber);
alshabib9ee68172014-09-09 14:45:14 -0700382 OFPortDesc port = findPort(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700383
384 OFPacketOut pkt = this.createLLDPPacketOut(port);
385 this.sendMsg(pkt);
386 if (useBDDP) {
387 OFPacketOut bpkt = this.createBDDPPacketOut(port);
388 this.sendMsg(bpkt);
389 }
390
391 }
392 }
393 }
394
395 // reschedule timer
alshabibc944fd02014-09-10 17:55:17 -0700396 timeout = Timer.getTimer().newTimeout(this, this.probeRate,
alshabibdf652ad2014-09-09 11:53:19 -0700397 TimeUnit.MILLISECONDS);
398 }
399
400 public void removeAllPorts() {
alshabibc944fd02014-09-10 17:55:17 -0700401 for (OFPortDesc port : ports.values()) {
alshabiba159a322014-09-09 14:50:51 -0700402 removePort(port);
alshabibdf652ad2014-09-09 11:53:19 -0700403 }
404 }
405
alshabibc944fd02014-09-10 17:55:17 -0700406 public void stop() {
alshabibc944fd02014-09-10 17:55:17 -0700407 timeout.cancel();
alshabib89e43ef2014-09-16 10:36:34 -0700408 removeAllPorts();
alshabibc944fd02014-09-10 17:55:17 -0700409 }
410
alshabibdf652ad2014-09-09 11:53:19 -0700411}