blob: 671e6d069f3fe6298403d510b292ef782dcb6aab [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
alshabib7911a052014-10-16 17:49:37 -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
alshabib7911a052014-10-16 17:49:37 -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 */
Ray Milkey957390e2016-02-09 10:02:46 -080016package org.onosproject.provider.lldpcommon;
alshabib7911a052014-10-16 17:49:37 -070017
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070018import com.google.common.collect.Sets;
Ayaka Koshibe12c8c082015-12-08 12:48:46 -080019
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070020import io.netty.util.Timeout;
21import io.netty.util.TimerTask;
22
Jonathan Harte8600eb2015-01-12 10:30:45 -080023import org.onlab.packet.Ethernet;
Charles Chan928ff8b2017-05-04 12:22:54 -070024import org.onlab.packet.MacAddress;
Jonathan Harte8600eb2015-01-12 10:30:45 -080025import org.onlab.packet.ONOSLLDP;
26import org.onlab.util.Timer;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.net.ConnectPoint;
28import org.onosproject.net.Device;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Link.Type;
Thomas Vachuska05453c92015-09-09 14:40:49 -070031import org.onosproject.net.LinkKey;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.Port;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.link.DefaultLinkDescription;
35import org.onosproject.net.link.LinkDescription;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.packet.DefaultOutboundPacket;
37import org.onosproject.net.packet.OutboundPacket;
38import org.onosproject.net.packet.PacketContext;
Ayaka Koshibe48229222016-05-16 18:04:26 -070039import org.onosproject.net.link.ProbedLinkProvider;
alshabib7911a052014-10-16 17:49:37 -070040import org.slf4j.Logger;
41
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070042import java.nio.ByteBuffer;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070043import java.util.Set;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070044
Thomas Vachuska586bc4f2016-08-19 12:44:26 -070045import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070046import static java.util.concurrent.TimeUnit.MILLISECONDS;
47import static org.onosproject.net.PortNumber.portNumber;
48import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
49import static org.slf4j.LoggerFactory.getLogger;
50
alshabib7911a052014-10-16 17:49:37 -070051/**
52 * Run discovery process from a physical switch. Ports are initially labeled as
53 * slow ports. When an LLDP is successfully received, label the remote port as
54 * fast. Every probeRate milliseconds, loop over all fast ports and send an
55 * LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
56 * discovery implementation.
alshabib7911a052014-10-16 17:49:37 -070057 */
Ray Milkey957390e2016-02-09 10:02:46 -080058public class LinkDiscovery implements TimerTask {
alshabib7911a052014-10-16 17:49:37 -070059
alshabib7911a052014-10-16 17:49:37 -070060 private final Logger log = getLogger(getClass());
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070061
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070062 private final Device device;
Ray Milkey957390e2016-02-09 10:02:46 -080063 private final LinkDiscoveryContext context;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070064
alshabib7911a052014-10-16 17:49:37 -070065 private final Ethernet ethPacket;
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -070066 private final Ethernet bddpEth;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070067
68 private Timeout timeout;
69 private volatile boolean isStopped;
Thomas Vachuska05453c92015-09-09 14:40:49 -070070 // Set of ports to be probed
71 private final Set<Long> ports = Sets.newConcurrentHashSet();
72
alshabib7911a052014-10-16 17:49:37 -070073 /**
74 * Instantiates discovery manager for the given physical switch. Creates a
75 * generic LLDP packet that will be customized for the port it is sent out on.
76 * Starts the the timer for the discovery process.
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -070077 *
Thomas Vachuska05453c92015-09-09 14:40:49 -070078 * @param device the physical switch
79 * @param context discovery context
alshabib7911a052014-10-16 17:49:37 -070080 */
Ray Milkey957390e2016-02-09 10:02:46 -080081 public LinkDiscovery(Device device, LinkDiscoveryContext context) {
alshabib7911a052014-10-16 17:49:37 -070082 this.device = device;
Thomas Vachuska05453c92015-09-09 14:40:49 -070083 this.context = context;
alshabib3d643ec2014-10-22 18:33:00 -070084
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070085 ethPacket = new Ethernet();
86 ethPacket.setEtherType(Ethernet.TYPE_LLDP);
Charles Chan928ff8b2017-05-04 12:22:54 -070087 ethPacket.setDestinationMACAddress(MacAddress.ONOS_LLDP);
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070088 ethPacket.setPad(true);
alshabib7911a052014-10-16 17:49:37 -070089
Thomas Vachuska05453c92015-09-09 14:40:49 -070090 bddpEth = new Ethernet();
Thomas Vachuska05453c92015-09-09 14:40:49 -070091 bddpEth.setEtherType(Ethernet.TYPE_BSN);
Charles Chan928ff8b2017-05-04 12:22:54 -070092 bddpEth.setDestinationMACAddress(MacAddress.BROADCAST);
Thomas Vachuska05453c92015-09-09 14:40:49 -070093 bddpEth.setPad(true);
alshabib7911a052014-10-16 17:49:37 -070094
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070095 isStopped = true;
alshabib7911a052014-10-16 17:49:37 -070096 start();
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070097 log.debug("Started discovery manager for switch {}", device.id());
alshabib7911a052014-10-16 17:49:37 -070098
99 }
100
Ray Milkey957390e2016-02-09 10:02:46 -0800101 public synchronized void stop() {
Jon Hall3edc08a2015-09-14 16:59:07 -0700102 if (!isStopped) {
103 isStopped = true;
104 timeout.cancel();
105 } else {
106 log.warn("LinkDiscovery stopped multiple times?");
107 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700108 }
109
Ray Milkey957390e2016-02-09 10:02:46 -0800110 public synchronized void start() {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700111 if (isStopped) {
112 isStopped = false;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700113 timeout = Timer.newTimeout(this, 0, MILLISECONDS);
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700114 } else {
115 log.warn("LinkDiscovery started multiple times?");
116 }
117 }
118
Ray Milkey957390e2016-02-09 10:02:46 -0800119 public synchronized boolean isStopped() {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700120 return isStopped || timeout.isCancelled();
121 }
122
alshabib7911a052014-10-16 17:49:37 -0700123 /**
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700124 * Add physical port to discovery process.
alshabib7911a052014-10-16 17:49:37 -0700125 * Send out initial LLDP and label it as slow port.
126 *
127 * @param port the port
128 */
Ray Milkey957390e2016-02-09 10:02:46 -0800129 public void addPort(Port port) {
Thomas Vachuska05453c92015-09-09 14:40:49 -0700130 boolean newPort = ports.add(port.number().toLong());
131 boolean isMaster = context.mastershipService().isLocalMaster(device.id());
Jonathan Hart45066bc2015-07-28 11:18:34 -0700132 if (newPort && isMaster) {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700133 log.debug("Sending initial probe to port {}@{}", port.number().toLong(), device.id());
Jonathan Hart45066bc2015-07-28 11:18:34 -0700134 sendProbes(port.number().toLong());
alshabib7911a052014-10-16 17:49:37 -0700135 }
alshabib7911a052014-10-16 17:49:37 -0700136 }
137
138 /**
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700139 * removed physical port from discovery process.
140 * @param port the port number
141 */
Ray Milkey957390e2016-02-09 10:02:46 -0800142 public void removePort(PortNumber port) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700143 ports.remove(port.toLong());
144 }
145
146 /**
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700147 * Handles an incoming LLDP packet. Creates link in topology and adds the
148 * link for staleness tracking.
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700149 *
Thomas Vachuska05453c92015-09-09 14:40:49 -0700150 * @param packetContext packet context
Yuta HIGUCHI5c947272014-11-03 21:39:21 -0800151 * @return true if handled
alshabib7911a052014-10-16 17:49:37 -0700152 */
Ray Milkey957390e2016-02-09 10:02:46 -0800153 public boolean handleLldp(PacketContext packetContext) {
Thomas Vachuska05453c92015-09-09 14:40:49 -0700154 Ethernet eth = packetContext.inPacket().parsed();
Jonathan Harte8600eb2015-01-12 10:30:45 -0800155 if (eth == null) {
156 return false;
157 }
158
alshabib7911a052014-10-16 17:49:37 -0700159 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
160 if (onoslldp != null) {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700161 Type lt;
162 if (notMy(eth.getSourceMAC().toString())) {
163 lt = Type.EDGE;
164 } else {
165 lt = eth.getEtherType() == Ethernet.TYPE_LLDP ?
166 Type.DIRECT : Type.INDIRECT;
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800167 }
168
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700169 PortNumber srcPort = portNumber(onoslldp.getPort());
Thomas Vachuska05453c92015-09-09 14:40:49 -0700170 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700171
Thomas Vachuska586bc4f2016-08-19 12:44:26 -0700172 String idString = onoslldp.getDeviceString();
173 if (!isNullOrEmpty(idString)) {
174 DeviceId srcDeviceId = DeviceId.deviceId(idString);
175 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
alshabib7911a052014-10-16 17:49:37 -0700176
Thomas Vachuska586bc4f2016-08-19 12:44:26 -0700177 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
178 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
179
180 LinkDescription ld = new DefaultLinkDescription(src, dst, lt);
181 try {
182 context.providerService().linkDetected(ld);
183 context.touchLink(LinkKey.linkKey(src, dst));
184 } catch (IllegalStateException e) {
185 return true;
186 }
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700187 return true;
188 }
alshabib7911a052014-10-16 17:49:37 -0700189 }
190 return false;
191 }
192
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800193 // true if *NOT* this cluster's own probe.
Ayaka Koshibe48229222016-05-16 18:04:26 -0700194 private boolean notMy(String mac) {
195 // if we are using DEFAULT_MAC, clustering hadn't initialized, so conservative 'yes'
196 String ourMac = context.fingerprint();
197 if (ProbedLinkProvider.defaultMac().equalsIgnoreCase(ourMac)) {
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800198 return true;
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800199 }
Ayaka Koshibe48229222016-05-16 18:04:26 -0700200 return !mac.equalsIgnoreCase(ourMac);
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800201 }
alshabib7911a052014-10-16 17:49:37 -0700202
alshabib7911a052014-10-16 17:49:37 -0700203 /**
204 * Execute this method every t milliseconds. Loops over all ports
205 * labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
206 * port.
207 *
208 * @param t timeout
alshabib7911a052014-10-16 17:49:37 -0700209 */
210 @Override
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700211 public void run(Timeout t) {
Yuta HIGUCHI34198582014-11-10 16:24:58 -0800212 if (isStopped()) {
213 return;
214 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700215
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700216 if (context.mastershipService().isLocalMaster(device.id())) {
217 log.trace("Sending probes from {}", device.id());
218 ports.forEach(this::sendProbes);
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700219 }
220
Yuta HIGUCHI32255782014-11-04 22:32:14 -0800221 if (!isStopped()) {
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700222 timeout = t.timer().newTimeout(this, context.probeRate(), MILLISECONDS);
Yuta HIGUCHI32255782014-11-04 22:32:14 -0800223 }
alshabib7911a052014-10-16 17:49:37 -0700224 }
225
alshabib7911a052014-10-16 17:49:37 -0700226 /**
227 * Creates packet_out LLDP for specified output port.
228 *
229 * @param port the port
230 * @return Packet_out message with LLDP data
231 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800232 private OutboundPacket createOutBoundLldp(Long port) {
alshabib7911a052014-10-16 17:49:37 -0700233 if (port == null) {
234 return null;
235 }
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800236 ONOSLLDP lldp = getLinkProbe(port);
Ayaka Koshibe48229222016-05-16 18:04:26 -0700237 ethPacket.setSourceMACAddress(context.fingerprint()).setPayload(lldp);
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700238 return new DefaultOutboundPacket(device.id(),
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700239 builder().setOutput(portNumber(port)).build(),
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700240 ByteBuffer.wrap(ethPacket.serialize()));
alshabib7911a052014-10-16 17:49:37 -0700241 }
242
243 /**
244 * Creates packet_out BDDP for specified output port.
245 *
246 * @param port the port
247 * @return Packet_out message with LLDP data
248 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800249 private OutboundPacket createOutBoundBddp(Long port) {
alshabib7911a052014-10-16 17:49:37 -0700250 if (port == null) {
251 return null;
252 }
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800253 ONOSLLDP lldp = getLinkProbe(port);
Ayaka Koshibe48229222016-05-16 18:04:26 -0700254 bddpEth.setSourceMACAddress(context.fingerprint()).setPayload(lldp);
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700255 return new DefaultOutboundPacket(device.id(),
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700256 builder().setOutput(portNumber(port)).build(),
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700257 ByteBuffer.wrap(bddpEth.serialize()));
alshabib7911a052014-10-16 17:49:37 -0700258 }
259
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800260 private ONOSLLDP getLinkProbe(Long port) {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700261 return ONOSLLDP.onosLLDP(device.id().toString(), device.chassisId(), port.intValue());
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800262 }
263
alshabib7911a052014-10-16 17:49:37 -0700264 private void sendProbes(Long portNumber) {
Ray Milkeyd9bbde82016-06-09 11:35:00 -0700265 if (context.packetService() == null) {
266 return;
267 }
Jon Hall274cecb2017-08-09 12:15:48 -0700268 log.trace("Sending probes out of {}@{}", portNumber, device.id());
Jonathan Hartb35540a2015-11-17 09:30:56 -0800269 OutboundPacket pkt = createOutBoundLldp(portNumber);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700270 context.packetService().emit(pkt);
Jonathan Hartb35540a2015-11-17 09:30:56 -0800271 if (context.useBddp()) {
272 OutboundPacket bpkt = createOutBoundBddp(portNumber);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700273 context.packetService().emit(bpkt);
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700274 }
alshabib7911a052014-10-16 17:49:37 -0700275 }
276
Ray Milkey957390e2016-02-09 10:02:46 -0800277 public boolean containsPort(long portNumber) {
Thomas Vachuska05453c92015-09-09 14:40:49 -0700278 return ports.contains(portNumber);
279 }
alshabib7911a052014-10-16 17:49:37 -0700280}