blob: 33e5f4d46fa457613ee86cdf20e849465ec89e30 [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
Ray Milkey1ab441b2018-03-06 15:33:44 -080018import com.google.common.collect.ImmutableMap;
jaegonkimd9119d0d2018-02-06 23:16:52 +090019import com.google.common.collect.Maps;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -070020import io.netty.util.Timeout;
21import io.netty.util.TimerTask;
jaegonkimaaa4f832018-03-11 00:48:36 +090022import io.netty.util.internal.StringUtil;
David K. Bainbridgebbbb9422018-11-05 14:52:38 -080023import org.onlab.packet.ChassisId;
Jonathan Harte8600eb2015-01-12 10:30:45 -080024import org.onlab.packet.Ethernet;
Charles Chan928ff8b2017-05-04 12:22:54 -070025import org.onlab.packet.MacAddress;
Jonathan Harte8600eb2015-01-12 10:30:45 -080026import org.onlab.packet.ONOSLLDP;
27import org.onlab.util.Timer;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.Device;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link.Type;
Thomas Vachuska05453c92015-09-09 14:40:49 -070032import org.onosproject.net.LinkKey;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.Port;
34import org.onosproject.net.PortNumber;
35import org.onosproject.net.link.DefaultLinkDescription;
36import org.onosproject.net.link.LinkDescription;
Ray Milkey44ba9372018-05-07 09:37:37 -070037import org.onosproject.net.link.ProbedLinkProvider;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.packet.DefaultOutboundPacket;
39import org.onosproject.net.packet.OutboundPacket;
40import org.onosproject.net.packet.PacketContext;
alshabib7911a052014-10-16 17:49:37 -070041import org.slf4j.Logger;
42
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070043import java.nio.ByteBuffer;
Ray Milkey44ba9372018-05-07 09:37:37 -070044import java.util.Map;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070045
Thomas Vachuska586bc4f2016-08-19 12:44:26 -070046import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070047import static java.util.concurrent.TimeUnit.MILLISECONDS;
jaegonkimd9119d0d2018-02-06 23:16:52 +090048import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070049import static org.onosproject.net.PortNumber.portNumber;
50import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
51import static org.slf4j.LoggerFactory.getLogger;
52
alshabib7911a052014-10-16 17:49:37 -070053/**
54 * Run discovery process from a physical switch. Ports are initially labeled as
55 * slow ports. When an LLDP is successfully received, label the remote port as
56 * fast. Every probeRate milliseconds, loop over all fast ports and send an
57 * LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
58 * discovery implementation.
alshabib7911a052014-10-16 17:49:37 -070059 */
Ray Milkey957390e2016-02-09 10:02:46 -080060public class LinkDiscovery implements TimerTask {
alshabib7911a052014-10-16 17:49:37 -070061
alshabib7911a052014-10-16 17:49:37 -070062 private final Logger log = getLogger(getClass());
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070063
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070064 private final Device device;
Ray Milkey957390e2016-02-09 10:02:46 -080065 private final LinkDiscoveryContext context;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070066
alshabib7911a052014-10-16 17:49:37 -070067 private final Ethernet ethPacket;
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -070068 private final Ethernet bddpEth;
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070069
70 private Timeout timeout;
71 private volatile boolean isStopped;
Thomas Vachuska9280e192018-03-06 11:19:29 -080072
Thomas Vachuska05453c92015-09-09 14:40:49 -070073 // Set of ports to be probed
Ray Milkey44ba9372018-05-07 09:37:37 -070074 private final Map<Long, String> portMap = Maps.newConcurrentMap();
alshabib7911a052014-10-16 17:49:37 -070075 /**
76 * Instantiates discovery manager for the given physical switch. Creates a
77 * generic LLDP packet that will be customized for the port it is sent out on.
78 * Starts the the timer for the discovery process.
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -070079 *
Thomas Vachuska05453c92015-09-09 14:40:49 -070080 * @param device the physical switch
81 * @param context discovery context
alshabib7911a052014-10-16 17:49:37 -070082 */
Ray Milkey957390e2016-02-09 10:02:46 -080083 public LinkDiscovery(Device device, LinkDiscoveryContext context) {
alshabib7911a052014-10-16 17:49:37 -070084 this.device = device;
Thomas Vachuska05453c92015-09-09 14:40:49 -070085 this.context = context;
alshabib3d643ec2014-10-22 18:33:00 -070086
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070087 ethPacket = new Ethernet();
88 ethPacket.setEtherType(Ethernet.TYPE_LLDP);
Charles Chan928ff8b2017-05-04 12:22:54 -070089 ethPacket.setDestinationMACAddress(MacAddress.ONOS_LLDP);
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070090 ethPacket.setPad(true);
alshabib7911a052014-10-16 17:49:37 -070091
Thomas Vachuska05453c92015-09-09 14:40:49 -070092 bddpEth = new Ethernet();
Thomas Vachuska05453c92015-09-09 14:40:49 -070093 bddpEth.setEtherType(Ethernet.TYPE_BSN);
Charles Chan928ff8b2017-05-04 12:22:54 -070094 bddpEth.setDestinationMACAddress(MacAddress.BROADCAST);
Thomas Vachuska05453c92015-09-09 14:40:49 -070095 bddpEth.setPad(true);
alshabib7911a052014-10-16 17:49:37 -070096
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070097 isStopped = true;
alshabib7911a052014-10-16 17:49:37 -070098 start();
Thomas Vachuska96f3ea72015-09-08 13:50:12 -070099 log.debug("Started discovery manager for switch {}", device.id());
alshabib7911a052014-10-16 17:49:37 -0700100
101 }
102
Ray Milkey957390e2016-02-09 10:02:46 -0800103 public synchronized void stop() {
Jon Hall3edc08a2015-09-14 16:59:07 -0700104 if (!isStopped) {
105 isStopped = true;
106 timeout.cancel();
107 } else {
108 log.warn("LinkDiscovery stopped multiple times?");
109 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700110 }
111
Ray Milkey957390e2016-02-09 10:02:46 -0800112 public synchronized void start() {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700113 if (isStopped) {
114 isStopped = false;
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700115 timeout = Timer.newTimeout(this, 0, MILLISECONDS);
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700116 } else {
117 log.warn("LinkDiscovery started multiple times?");
118 }
119 }
120
Ray Milkey957390e2016-02-09 10:02:46 -0800121 public synchronized boolean isStopped() {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700122 return isStopped || timeout.isCancelled();
123 }
124
alshabib7911a052014-10-16 17:49:37 -0700125 /**
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700126 * Add physical port to discovery process.
alshabib7911a052014-10-16 17:49:37 -0700127 * Send out initial LLDP and label it as slow port.
128 *
129 * @param port the port
130 */
Ray Milkey957390e2016-02-09 10:02:46 -0800131 public void addPort(Port port) {
jaegonkimd9119d0d2018-02-06 23:16:52 +0900132 Long portNum = port.number().toLong();
133 String portName = port.annotations().value(PORT_NAME);
jaegonkimaaa4f832018-03-11 00:48:36 +0900134 if (portName == null) {
135 portName = StringUtil.EMPTY_STRING;
136 }
jaegonkimd9119d0d2018-02-06 23:16:52 +0900137
You Wang168aa622018-03-08 14:01:37 -0800138 boolean newPort = !containsPort(portNum);
jaegonkimd9119d0d2018-02-06 23:16:52 +0900139 portMap.put(portNum, portName);
140
Thomas Vachuska05453c92015-09-09 14:40:49 -0700141 boolean isMaster = context.mastershipService().isLocalMaster(device.id());
Jonathan Hart45066bc2015-07-28 11:18:34 -0700142 if (newPort && isMaster) {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700143 log.debug("Sending initial probe to port {}@{}", port.number().toLong(), device.id());
jaegonkimd9119d0d2018-02-06 23:16:52 +0900144 sendProbes(portNum, portName);
alshabib7911a052014-10-16 17:49:37 -0700145 }
alshabib7911a052014-10-16 17:49:37 -0700146 }
147
148 /**
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700149 * removed physical port from discovery process.
150 * @param port the port number
151 */
Ray Milkey957390e2016-02-09 10:02:46 -0800152 public void removePort(PortNumber port) {
jaegonkimd9119d0d2018-02-06 23:16:52 +0900153 portMap.remove(port.toLong());
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700154 }
155
156 /**
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700157 * Handles an incoming LLDP packet. Creates link in topology and adds the
158 * link for staleness tracking.
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700159 *
Thomas Vachuska05453c92015-09-09 14:40:49 -0700160 * @param packetContext packet context
Yuta HIGUCHI5c947272014-11-03 21:39:21 -0800161 * @return true if handled
alshabib7911a052014-10-16 17:49:37 -0700162 */
Ray Milkey957390e2016-02-09 10:02:46 -0800163 public boolean handleLldp(PacketContext packetContext) {
Thomas Vachuska05453c92015-09-09 14:40:49 -0700164 Ethernet eth = packetContext.inPacket().parsed();
Jonathan Harte8600eb2015-01-12 10:30:45 -0800165 if (eth == null) {
166 return false;
167 }
168
alshabib7911a052014-10-16 17:49:37 -0700169 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
170 if (onoslldp != null) {
Ayaka Koshibe48229222016-05-16 18:04:26 -0700171 Type lt;
172 if (notMy(eth.getSourceMAC().toString())) {
173 lt = Type.EDGE;
174 } else {
175 lt = eth.getEtherType() == Ethernet.TYPE_LLDP ?
176 Type.DIRECT : Type.INDIRECT;
Samuel Jero31e16f52018-09-21 10:34:28 -0400177
178 /* Verify MAC in LLDP packets */
179 if (!ONOSLLDP.verify(onoslldp, context.lldpSecret(), context.maxDiscoveryDelay())) {
180 log.warn("LLDP Packet failed to validate!");
181 return true;
182 }
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800183 }
184
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700185 PortNumber srcPort = portNumber(onoslldp.getPort());
Thomas Vachuska05453c92015-09-09 14:40:49 -0700186 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700187
Thomas Vachuska586bc4f2016-08-19 12:44:26 -0700188 String idString = onoslldp.getDeviceString();
189 if (!isNullOrEmpty(idString)) {
190 DeviceId srcDeviceId = DeviceId.deviceId(idString);
191 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
alshabib7911a052014-10-16 17:49:37 -0700192
Thomas Vachuska586bc4f2016-08-19 12:44:26 -0700193 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
194 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
195
196 LinkDescription ld = new DefaultLinkDescription(src, dst, lt);
197 try {
198 context.providerService().linkDetected(ld);
199 context.touchLink(LinkKey.linkKey(src, dst));
200 } catch (IllegalStateException e) {
201 return true;
202 }
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700203 return true;
204 }
alshabib7911a052014-10-16 17:49:37 -0700205 }
206 return false;
207 }
208
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800209 // true if *NOT* this cluster's own probe.
Ayaka Koshibe48229222016-05-16 18:04:26 -0700210 private boolean notMy(String mac) {
211 // if we are using DEFAULT_MAC, clustering hadn't initialized, so conservative 'yes'
212 String ourMac = context.fingerprint();
213 if (ProbedLinkProvider.defaultMac().equalsIgnoreCase(ourMac)) {
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800214 return true;
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800215 }
Ayaka Koshibe48229222016-05-16 18:04:26 -0700216 return !mac.equalsIgnoreCase(ourMac);
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800217 }
alshabib7911a052014-10-16 17:49:37 -0700218
alshabib7911a052014-10-16 17:49:37 -0700219 /**
220 * Execute this method every t milliseconds. Loops over all ports
221 * labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
222 * port.
223 *
224 * @param t timeout
alshabib7911a052014-10-16 17:49:37 -0700225 */
226 @Override
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700227 public void run(Timeout t) {
Yuta HIGUCHI34198582014-11-10 16:24:58 -0800228 if (isStopped()) {
229 return;
230 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700231
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700232 if (context.mastershipService().isLocalMaster(device.id())) {
233 log.trace("Sending probes from {}", device.id());
Ray Milkey1ab441b2018-03-06 15:33:44 -0800234 ImmutableMap.copyOf(portMap).forEach(this::sendProbes);
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700235 }
236
Yuta HIGUCHI32255782014-11-04 22:32:14 -0800237 if (!isStopped()) {
Yuta HIGUCHI19afc032017-05-20 23:44:17 -0700238 timeout = t.timer().newTimeout(this, context.probeRate(), MILLISECONDS);
Yuta HIGUCHI32255782014-11-04 22:32:14 -0800239 }
alshabib7911a052014-10-16 17:49:37 -0700240 }
241
alshabib7911a052014-10-16 17:49:37 -0700242 /**
243 * Creates packet_out LLDP for specified output port.
244 *
jaegonkimd9119d0d2018-02-06 23:16:52 +0900245 * @param portNumber the port
246 * @param portDesc the port description
alshabib7911a052014-10-16 17:49:37 -0700247 * @return Packet_out message with LLDP data
248 */
jaegonkimd9119d0d2018-02-06 23:16:52 +0900249 private OutboundPacket createOutBoundLldp(Long portNumber, String portDesc) {
250 if (portNumber == null) {
alshabib7911a052014-10-16 17:49:37 -0700251 return null;
252 }
David K. Bainbridgebbbb9422018-11-05 14:52:38 -0800253 ONOSLLDP lldp = getLinkProbe(context.deviceService().getDevice(device.id()).chassisId(),
254 portNumber, portDesc);
Ayaka Koshibe48229222016-05-16 18:04:26 -0700255 ethPacket.setSourceMACAddress(context.fingerprint()).setPayload(lldp);
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700256 return new DefaultOutboundPacket(device.id(),
jaegonkimd9119d0d2018-02-06 23:16:52 +0900257 builder().setOutput(portNumber(portNumber)).build(),
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700258 ByteBuffer.wrap(ethPacket.serialize()));
alshabib7911a052014-10-16 17:49:37 -0700259 }
260
261 /**
262 * Creates packet_out BDDP for specified output port.
263 *
jaegonkimd9119d0d2018-02-06 23:16:52 +0900264 * @param portNumber the port
265 * @param portDesc the port description
alshabib7911a052014-10-16 17:49:37 -0700266 * @return Packet_out message with LLDP data
267 */
jaegonkimd9119d0d2018-02-06 23:16:52 +0900268 private OutboundPacket createOutBoundBddp(Long portNumber, String portDesc) {
269 if (portNumber == null) {
alshabib7911a052014-10-16 17:49:37 -0700270 return null;
271 }
David K. Bainbridgebbbb9422018-11-05 14:52:38 -0800272 ONOSLLDP lldp = getLinkProbe(context.deviceService().getDevice(device.id()).chassisId(),
273 portNumber, portDesc);
Ayaka Koshibe48229222016-05-16 18:04:26 -0700274 bddpEth.setSourceMACAddress(context.fingerprint()).setPayload(lldp);
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700275 return new DefaultOutboundPacket(device.id(),
jaegonkimd9119d0d2018-02-06 23:16:52 +0900276 builder().setOutput(portNumber(portNumber)).build(),
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700277 ByteBuffer.wrap(bddpEth.serialize()));
alshabib7911a052014-10-16 17:49:37 -0700278 }
279
David K. Bainbridgebbbb9422018-11-05 14:52:38 -0800280 private ONOSLLDP getLinkProbe(ChassisId chassisId, Long portNumber, String portDesc) {
281 return ONOSLLDP.onosSecureLLDP(device.id().toString(), chassisId, portNumber.intValue(),
Samuel Jero31e16f52018-09-21 10:34:28 -0400282 context.lldpSecret());
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800283 }
284
jaegonkimd9119d0d2018-02-06 23:16:52 +0900285 private void sendProbes(Long portNumber, String portDesc) {
Ray Milkeyd9bbde82016-06-09 11:35:00 -0700286 if (context.packetService() == null) {
287 return;
288 }
Jon Hall274cecb2017-08-09 12:15:48 -0700289 log.trace("Sending probes out of {}@{}", portNumber, device.id());
jaegonkimd9119d0d2018-02-06 23:16:52 +0900290 OutboundPacket pkt = createOutBoundLldp(portNumber, portDesc);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700291 context.packetService().emit(pkt);
Jonathan Hartb35540a2015-11-17 09:30:56 -0800292 if (context.useBddp()) {
jaegonkimd9119d0d2018-02-06 23:16:52 +0900293 OutboundPacket bpkt = createOutBoundBddp(portNumber, portDesc);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700294 context.packetService().emit(bpkt);
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700295 }
alshabib7911a052014-10-16 17:49:37 -0700296 }
297
Ray Milkey957390e2016-02-09 10:02:46 -0800298 public boolean containsPort(long portNumber) {
jaegonkimd9119d0d2018-02-06 23:16:52 +0900299 return portMap.containsKey(portNumber);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700300 }
alshabib7911a052014-10-16 17:49:37 -0700301}