blob: 00c7305bd51f7fd8171e0ae8456b7a15b8e9fed2 [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.
alshabibdf652ad2014-09-09 11:53:19 -070065 */
alshabib75e77422014-10-16 18:03:40 -070066@Deprecated
alshabibdf652ad2014-09-09 11:53:19 -070067public class LinkDiscovery implements TimerTask {
68
69 private final OpenFlowSwitch sw;
70 // send 1 probe every probeRate milliseconds
71 private final long probeRate;
72 private final Set<Integer> slowPorts;
73 private final Set<Integer> fastPorts;
74 // number of unacknowledged probes per port
75 private final Map<Integer, AtomicInteger> portProbeCount;
76 // number of probes to send before link is removed
77 private static final short MAX_PROBE_COUNT = 3;
78 private Iterator<Integer> slowIterator;
79 private final OFFactory ofFactory;
80 private final Logger log = getLogger(getClass());
81 private final ONLabLddp lldpPacket;
82 private final Ethernet ethPacket;
83 private Ethernet bddpEth;
84 private final boolean useBDDP;
85 private final OpenFlowController ctrl;
86 private final LinkProviderService linkProvider;
alshabib64231d82014-09-25 18:25:31 -070087 protected final Map<Integer, OFPortDesc> ports;
alshabibc944fd02014-09-10 17:55:17 -070088 private Timeout timeout;
alshabibdf652ad2014-09-09 11:53:19 -070089
Yuta HIGUCHI5c947272014-11-03 21:39:21 -080090 /*
alshabibdf652ad2014-09-09 11:53:19 -070091 * Instantiates discovery manager for the given physical switch. Creates a
92 * generic LLDP packet that will be customized for the port it is sent out on.
93 * Starts the the timer for the discovery process.
94 *
95 * @param sw the physical switch
96 * @param useBDDP flag to also use BDDP for discovery
97 */
98 public LinkDiscovery(final OpenFlowSwitch sw,
99 OpenFlowController ctrl, LinkProviderService providerService, Boolean... useBDDP) {
100 this.sw = sw;
101 this.ofFactory = sw.factory();
102 this.ctrl = ctrl;
alshabib7674db42014-09-12 23:40:46 -0700103 this.probeRate = 3000;
alshabibdf652ad2014-09-09 11:53:19 -0700104 this.linkProvider = providerService;
105 this.slowPorts = Collections.synchronizedSet(new HashSet<Integer>());
106 this.fastPorts = Collections.synchronizedSet(new HashSet<Integer>());
alshabib9ee68172014-09-09 14:45:14 -0700107 this.ports = new ConcurrentHashMap<>();
alshabibdf652ad2014-09-09 11:53:19 -0700108 this.portProbeCount = new HashMap<Integer, AtomicInteger>();
109 this.lldpPacket = new ONLabLddp();
110 this.lldpPacket.setSwitch(this.sw.getId());
111 this.ethPacket = new Ethernet();
112 this.ethPacket.setEtherType(Ethernet.TYPE_LLDP);
113 this.ethPacket.setDestinationMACAddress(ONLabLddp.LLDP_NICIRA);
114 this.ethPacket.setPayload(this.lldpPacket);
115 this.ethPacket.setPad(true);
116 this.useBDDP = useBDDP.length > 0 ? useBDDP[0] : false;
117 if (this.useBDDP) {
118 this.bddpEth = new Ethernet();
119 this.bddpEth.setPayload(this.lldpPacket);
120 this.bddpEth.setEtherType(Ethernet.TYPE_BSN);
121 this.bddpEth.setDestinationMACAddress(ONLabLddp.BDDP_MULTICAST);
122 this.bddpEth.setPad(true);
123 log.info("Using BDDP to discover network");
124 }
125 for (OFPortDesc port : sw.getPorts()) {
126 if (port.getPortNo() != OFPort.LOCAL) {
127 addPort(port);
128 }
129 }
alshabib7674db42014-09-12 23:40:46 -0700130 timeout = Timer.getTimer().newTimeout(this, 0,
alshabibdf652ad2014-09-09 11:53:19 -0700131 TimeUnit.MILLISECONDS);
alshabib6eb438a2014-10-01 16:39:37 -0700132 this.log.info("Started discovery manager for switch {}",
alshabibdf652ad2014-09-09 11:53:19 -0700133 sw.getId());
134
135 }
136
137 /**
138 * Add physical port port to discovery process.
139 * Send out initial LLDP and label it as slow port.
140 *
141 * @param port the port
142 */
143 public void addPort(final OFPortDesc port) {
144 // Ignore ports that are not on this switch, or already booted. */
alshabib9ee68172014-09-09 14:45:14 -0700145 this.ports.put(port.getPortNo().getPortNumber(), port);
alshabibdf652ad2014-09-09 11:53:19 -0700146 synchronized (this) {
147 this.log.debug("sending init probe to port {}",
148 port.getPortNo().getPortNumber());
149 OFPacketOut pkt;
150
151 pkt = this.createLLDPPacketOut(port);
152 this.sw.sendMsg(pkt);
153 if (useBDDP) {
154 OFPacketOut bpkt = this.createBDDPPacketOut(port);
155 this.sw.sendMsg(bpkt);
156 }
157
158 this.slowPorts.add(port.getPortNo().getPortNumber());
alshabibdf652ad2014-09-09 11:53:19 -0700159 }
160
161 }
162
163 /**
164 * Removes physical port from discovery process.
165 *
166 * @param port the port
167 */
alshabiba159a322014-09-09 14:50:51 -0700168 public void removePort(final OFPortDesc port) {
alshabibdf652ad2014-09-09 11:53:19 -0700169 // Ignore ports that are not on this switch
170
alshabiba159a322014-09-09 14:50:51 -0700171 int portnum = port.getPortNo().getPortNumber();
172 this.ports.remove(portnum);
alshabibdf652ad2014-09-09 11:53:19 -0700173 synchronized (this) {
174 if (this.slowPorts.contains(portnum)) {
175 this.slowPorts.remove(portnum);
alshabibdf652ad2014-09-09 11:53:19 -0700176
177 } else if (this.fastPorts.contains(portnum)) {
178 this.fastPorts.remove(portnum);
179 this.portProbeCount.remove(portnum);
180 // no iterator to update
181 } else {
182 this.log.warn(
183 "tried to dynamically remove non-existing port {}",
184 portnum);
185 }
186 }
alshabibc944fd02014-09-10 17:55:17 -0700187 ConnectPoint cp = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700188 DeviceId.deviceId(uri(sw.getId())),
alshabibc944fd02014-09-10 17:55:17 -0700189 PortNumber.portNumber(port.getPortNo().getPortNumber()));
190 linkProvider.linksVanished(cp);
alshabibdf652ad2014-09-09 11:53:19 -0700191
192 }
193
194 /**
195 * Method called by remote port to acknowledge receipt of LLDP sent by
196 * this port. If slow port, updates label to fast. If fast port, decrements
197 * number of unacknowledged probes.
198 *
199 * @param port the port
200 */
201 public void ackProbe(final Integer port) {
202 final int portNumber = port;
203 synchronized (this) {
204 if (this.slowPorts.contains(portNumber)) {
205 this.log.debug("Setting slow port to fast: {}:{}",
206 this.sw.getId(), portNumber);
207 this.slowPorts.remove(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700208 this.fastPorts.add(portNumber);
209 this.portProbeCount.put(portNumber, new AtomicInteger(0));
210 } else {
211 if (this.fastPorts.contains(portNumber)) {
212 this.portProbeCount.get(portNumber).set(0);
213 } else {
214 this.log.debug(
215 "Got ackProbe for non-existing port: {}",
216 portNumber);
217 }
218 }
219 }
220 }
221
222 /**
223 * Creates packet_out LLDP for specified output port.
224 *
225 * @param port the port
226 * @return Packet_out message with LLDP data
alshabibdf652ad2014-09-09 11:53:19 -0700227 */
228 private OFPacketOut createLLDPPacketOut(final OFPortDesc port) {
alshabib89e43ef2014-09-16 10:36:34 -0700229 if (port == null) {
230 return null;
231 }
alshabibdf652ad2014-09-09 11:53:19 -0700232 OFPacketOut.Builder packetOut = this.ofFactory.buildPacketOut();
233 packetOut.setBufferId(OFBufferId.NO_BUFFER);
234 OFAction act = this.ofFactory.actions().buildOutput()
235 .setPort(port.getPortNo()).build();
236 packetOut.setActions(Collections.singletonList(act));
237 this.lldpPacket.setPort(port.getPortNo().getPortNumber());
238 this.ethPacket.setSourceMACAddress(port.getHwAddr().getBytes());
239
240 final byte[] lldp = this.ethPacket.serialize();
241 packetOut.setData(lldp);
242 return packetOut.build();
243 }
244
245 /**
246 * Creates packet_out BDDP for specified output port.
247 *
248 * @param port the port
249 * @return Packet_out message with LLDP data
alshabibdf652ad2014-09-09 11:53:19 -0700250 */
251 private OFPacketOut createBDDPPacketOut(final OFPortDesc port) {
alshabib89e43ef2014-09-16 10:36:34 -0700252 if (port == null) {
253 return null;
254 }
alshabibdf652ad2014-09-09 11:53:19 -0700255 OFPacketOut.Builder packetOut = sw.factory().buildPacketOut();
256
257 packetOut.setBufferId(OFBufferId.NO_BUFFER);
258
259 OFActionOutput.Builder act = sw.factory().actions().buildOutput()
260 .setPort(port.getPortNo());
261 OFAction out = act.build();
262 packetOut.setActions(Collections.singletonList(out));
263 this.lldpPacket.setPort(port.getPortNo().getPortNumber());
264 this.bddpEth.setSourceMACAddress(port.getHwAddr().getBytes());
265
266 final byte[] bddp = this.bddpEth.serialize();
267 packetOut.setData(bddp);
268
269 return packetOut.build();
270 }
271
272
273 private void sendMsg(final OFMessage msg) {
alshabib89e43ef2014-09-16 10:36:34 -0700274 if (msg == null) {
275 return;
276 }
alshabibdf652ad2014-09-09 11:53:19 -0700277 this.sw.sendMsg(msg);
278 }
279
280 public String getName() {
281 return "LinkDiscovery " + this.sw.getStringId();
282 }
283
Yuta HIGUCHI5c947272014-11-03 21:39:21 -0800284 /*
alshabibdf652ad2014-09-09 11:53:19 -0700285 * Handles an incoming LLDP packet. Creates link in topology and sends ACK
286 * to port where LLDP originated.
287 */
alshabib505bc6b2014-09-09 15:04:13 -0700288 public boolean handleLLDP(final byte[] pkt, Integer inPort) {
alshabibdf652ad2014-09-09 11:53:19 -0700289
alshabib9ee68172014-09-09 14:45:14 -0700290 short ethType = ONLabLddp.isOVXLLDP(pkt);
291 if (ethType == Ethernet.TYPE_LLDP || ethType == Ethernet.TYPE_BSN) {
alshabibdf652ad2014-09-09 11:53:19 -0700292 final Integer dstPort = inPort;
293 final DPIDandPort dp = ONLabLddp.parseLLDP(pkt);
294 final OpenFlowSwitch srcSwitch = ctrl.getSwitch(new Dpid(dp.getDpid()));
295 final Integer srcPort = dp.getPort();
296 if (srcSwitch == null) {
alshabib505bc6b2014-09-09 15:04:13 -0700297 return true;
alshabibdf652ad2014-09-09 11:53:19 -0700298 }
299 this.ackProbe(srcPort);
300 ConnectPoint src = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700301 DeviceId.deviceId(uri(srcSwitch.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700302 PortNumber.portNumber(srcPort));
303
304 ConnectPoint dst = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700305 DeviceId.deviceId(uri(sw.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700306 PortNumber.portNumber(dstPort));
307 LinkDescription ld;
alshabib9ee68172014-09-09 14:45:14 -0700308 if (ethType == Ethernet.TYPE_BSN) {
alshabibdf652ad2014-09-09 11:53:19 -0700309 ld = new DefaultLinkDescription(src, dst, Type.INDIRECT);
310 } else {
311 ld = new DefaultLinkDescription(src, dst, Type.DIRECT);
312 }
313 linkProvider.linkDetected(ld);
alshabib505bc6b2014-09-09 15:04:13 -0700314 return true;
alshabibdf652ad2014-09-09 11:53:19 -0700315 } else {
316 this.log.debug("Ignoring unknown LLDP");
alshabib505bc6b2014-09-09 15:04:13 -0700317 return false;
alshabibdf652ad2014-09-09 11:53:19 -0700318 }
319 }
320
alshabib9ee68172014-09-09 14:45:14 -0700321 private OFPortDesc findPort(Integer inPort) {
322 return ports.get(inPort);
alshabibdf652ad2014-09-09 11:53:19 -0700323 }
324
325 /**
326 * Execute this method every t milliseconds. Loops over all ports
327 * labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
328 * port.
329 *
330 * @param t timeout
alshabibdf652ad2014-09-09 11:53:19 -0700331 */
332 @Override
333 public void run(final Timeout t) {
334 this.log.debug("sending probes");
335 synchronized (this) {
336 final Iterator<Integer> fastIterator = this.fastPorts.iterator();
337 while (fastIterator.hasNext()) {
338 final Integer portNumber = fastIterator.next();
Yuta HIGUCHI70ce8432014-10-15 23:59:10 -0700339 OFPortDesc port = findPort(portNumber);
340 if (port == null) {
341 // port can be null
342 // #removePort modifies `ports` outside synchronized block
343 continue;
344 }
alshabibdf652ad2014-09-09 11:53:19 -0700345 final int probeCount = this.portProbeCount.get(portNumber)
346 .getAndIncrement();
alshabibdf652ad2014-09-09 11:53:19 -0700347 if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
348 this.log.debug("sending fast probe to port");
349
350 OFPacketOut pkt = this.createLLDPPacketOut(port);
351 this.sendMsg(pkt);
352 if (useBDDP) {
353 OFPacketOut bpkt = this.createBDDPPacketOut(port);
354 this.sendMsg(bpkt);
355 }
356 } else {
357 // Update fast and slow ports
358 fastIterator.remove();
359 this.slowPorts.add(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700360 this.portProbeCount.remove(portNumber);
361
362 // Remove link from topology
363 final OFPortDesc srcPort = port;
364
365 ConnectPoint cp = new ConnectPoint(
tom782a7cf2014-09-11 23:58:38 -0700366 DeviceId.deviceId(uri(sw.getId())),
alshabibdf652ad2014-09-09 11:53:19 -0700367 PortNumber.portNumber(srcPort.getPortNo().getPortNumber()));
368 linkProvider.linksVanished(cp);
369 }
370 }
371
372 // send a probe for the next slow port
alshabib818d57c2014-09-15 22:34:30 -0700373 if (!this.slowPorts.isEmpty()) {
374 this.slowIterator = this.slowPorts.iterator();
375 while (this.slowIterator.hasNext()) {
alshabibdf652ad2014-09-09 11:53:19 -0700376 final int portNumber = this.slowIterator.next();
377 this.log.debug("sending slow probe to port {}", portNumber);
alshabib9ee68172014-09-09 14:45:14 -0700378 OFPortDesc port = findPort(portNumber);
alshabibdf652ad2014-09-09 11:53:19 -0700379
380 OFPacketOut pkt = this.createLLDPPacketOut(port);
381 this.sendMsg(pkt);
382 if (useBDDP) {
383 OFPacketOut bpkt = this.createBDDPPacketOut(port);
384 this.sendMsg(bpkt);
385 }
386
387 }
388 }
389 }
390
391 // reschedule timer
alshabibc944fd02014-09-10 17:55:17 -0700392 timeout = Timer.getTimer().newTimeout(this, this.probeRate,
alshabibdf652ad2014-09-09 11:53:19 -0700393 TimeUnit.MILLISECONDS);
394 }
395
396 public void removeAllPorts() {
alshabibc944fd02014-09-10 17:55:17 -0700397 for (OFPortDesc port : ports.values()) {
alshabiba159a322014-09-09 14:50:51 -0700398 removePort(port);
alshabibdf652ad2014-09-09 11:53:19 -0700399 }
400 }
401
alshabibc944fd02014-09-10 17:55:17 -0700402 public void stop() {
alshabibc944fd02014-09-10 17:55:17 -0700403 timeout.cancel();
alshabib89e43ef2014-09-16 10:36:34 -0700404 removeAllPorts();
alshabibc944fd02014-09-10 17:55:17 -0700405 }
406
alshabibdf652ad2014-09-09 11:53:19 -0700407}