blob: 4997775aa07007318ac36536fa64218c31c5e78a [file] [log] [blame]
Ray Milkeyb7f0f642016-01-22 16:08:14 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * 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
7 *
8 * 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.
15 */
16package org.onosproject.provider.netcfglinks;
17
18import java.nio.ByteBuffer;
19import java.util.Set;
20
21import org.jboss.netty.util.Timeout;
22import org.jboss.netty.util.TimerTask;
23import org.onlab.packet.Ethernet;
24import org.onlab.packet.ONOSLLDP;
25import org.onlab.util.Timer;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.Device;
28import org.onosproject.net.DeviceId;
29import org.onosproject.net.Link.Type;
30import org.onosproject.net.LinkKey;
31import org.onosproject.net.Port;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.link.DefaultLinkDescription;
34import org.onosproject.net.link.LinkDescription;
35import org.onosproject.net.packet.DefaultOutboundPacket;
36import org.onosproject.net.packet.OutboundPacket;
37import org.onosproject.net.packet.PacketContext;
38import org.slf4j.Logger;
39
40import com.google.common.collect.Sets;
41
42import static java.util.concurrent.TimeUnit.MILLISECONDS;
43import static org.onosproject.net.PortNumber.portNumber;
44import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
45import static org.slf4j.LoggerFactory.getLogger;
46
47/**
48 * Run discovery process from a physical switch. Ports are initially labeled as
49 * slow ports. When an LLDP is successfully received, label the remote port as
50 * fast. Every probeRate milliseconds, loop over all fast ports and send an
51 * LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
52 * discovery implementation.
53 */
54class LinkDiscovery implements TimerTask {
55
56 private final Logger log = getLogger(getClass());
57
58 private static final String SRC_MAC = "DE:AD:BE:EF:BA:11";
59
60 private final Device device;
61 private final DiscoveryContext context;
62
63 private final ONOSLLDP lldpPacket;
64 private final Ethernet ethPacket;
65 private final Ethernet bddpEth;
66
67 private Timeout timeout;
68 private volatile boolean isStopped;
69
70 // Set of ports to be probed
71 private final Set<Long> ports = Sets.newConcurrentHashSet();
72
73 /**
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.
77 *
78 * @param device the physical switch
79 * @param context discovery context
80 */
81 LinkDiscovery(Device device, DiscoveryContext context) {
82 this.device = device;
83 this.context = context;
84
85 lldpPacket = new ONOSLLDP();
86 lldpPacket.setChassisId(device.chassisId());
87 lldpPacket.setDevice(device.id().toString());
88
89 ethPacket = new Ethernet();
90 ethPacket.setEtherType(Ethernet.TYPE_LLDP);
91 ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
92 ethPacket.setPayload(this.lldpPacket);
93 ethPacket.setPad(true);
94
95 bddpEth = new Ethernet();
96 bddpEth.setPayload(lldpPacket);
97 bddpEth.setEtherType(Ethernet.TYPE_BSN);
98 bddpEth.setDestinationMACAddress(ONOSLLDP.BDDP_MULTICAST);
99 bddpEth.setPad(true);
100
101 isStopped = true;
102 start();
103 log.debug("Started discovery manager for switch {}", device.id());
104
105 }
106
107 synchronized void stop() {
108 if (!isStopped) {
109 isStopped = true;
110 timeout.cancel();
111 } else {
112 log.warn("LinkDiscovery stopped multiple times?");
113 }
114 }
115
116 synchronized void start() {
117 if (isStopped) {
118 isStopped = false;
119 timeout = Timer.getTimer().newTimeout(this, 0, MILLISECONDS);
120 } else {
121 log.warn("LinkDiscovery started multiple times?");
122 }
123 }
124
125 synchronized boolean isStopped() {
126 return isStopped || timeout.isCancelled();
127 }
128
129 /**
130 * Add physical port to discovery process.
131 * Send out initial LLDP and label it as slow port.
132 *
133 * @param port the port
134 */
135 void addPort(Port port) {
136 boolean newPort = ports.add(port.number().toLong());
137 boolean isMaster = context.mastershipService().isLocalMaster(device.id());
138 if (newPort && isMaster) {
139 log.debug("Sending initial probe to port {}@{}", port.number().toLong(), device.id());
140 sendProbes(port.number().toLong());
141 }
142 }
143
144 /**
145 * removed physical port from discovery process.
146 * @param port the port number
147 */
148 void removePort(PortNumber port) {
149 ports.remove(port.toLong());
150 }
151
152 /**
153 * Handles an incoming LLDP packet. Creates link in topology and adds the
154 * link for staleness tracking.
155 *
156 * @param packetContext packet context
157 * @return true if handled
158 */
159 boolean handleLldp(PacketContext packetContext) {
160 Ethernet eth = packetContext.inPacket().parsed();
161 if (eth == null) {
162 return false;
163 }
164
165 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
166 if (onoslldp != null) {
167 PortNumber srcPort = portNumber(onoslldp.getPort());
168 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
169 DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
170 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
171
172 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
173 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
174
175 LinkDescription ld = eth.getEtherType() == Ethernet.TYPE_LLDP ?
176 new DefaultLinkDescription(src, dst, Type.DIRECT, DefaultLinkDescription.EXPECTED) :
177 new DefaultLinkDescription(src, dst, Type.INDIRECT, DefaultLinkDescription.EXPECTED);
178
179 try {
180 context.providerService().linkDetected(ld);
181 context.touchLink(LinkKey.linkKey(src, dst));
182 } catch (IllegalStateException e) {
183 return true;
184 }
185 return true;
186 }
187 return false;
188 }
189
190
191 /**
192 * Execute this method every t milliseconds. Loops over all ports
193 * labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
194 * port.
195 *
196 * @param t timeout
197 */
198 @Override
199 public void run(Timeout t) {
200 if (isStopped()) {
201 return;
202 }
203
204 if (context.mastershipService().isLocalMaster(device.id())) {
205 log.trace("Sending probes from {}", device.id());
206 ports.forEach(this::sendProbes);
207 }
208
209 if (!isStopped()) {
210 timeout = Timer.getTimer().newTimeout(this, context.probeRate(), MILLISECONDS);
211 }
212 }
213
214 /**
215 * Creates packet_out LLDP for specified output port.
216 *
217 * @param port the port
218 * @return Packet_out message with LLDP data
219 */
220 private OutboundPacket createOutBoundLldp(Long port) {
221 if (port == null) {
222 return null;
223 }
224 lldpPacket.setPortId(port.intValue());
225 ethPacket.setSourceMACAddress(SRC_MAC);
226 return new DefaultOutboundPacket(device.id(),
227 builder().setOutput(portNumber(port)).build(),
228 ByteBuffer.wrap(ethPacket.serialize()));
229 }
230
231 /**
232 * Creates packet_out BDDP for specified output port.
233 *
234 * @param port the port
235 * @return Packet_out message with LLDP data
236 */
237 private OutboundPacket createOutBoundBddp(Long port) {
238 if (port == null) {
239 return null;
240 }
241 lldpPacket.setPortId(port.intValue());
242 bddpEth.setSourceMACAddress(SRC_MAC);
243 return new DefaultOutboundPacket(device.id(),
244 builder().setOutput(portNumber(port)).build(),
245 ByteBuffer.wrap(bddpEth.serialize()));
246 }
247
248 private void sendProbes(Long portNumber) {
249 log.trace("Sending probes out to {}@{}", portNumber, device.id());
250 OutboundPacket pkt = createOutBoundLldp(portNumber);
251 context.packetService().emit(pkt);
252 if (context.useBddp()) {
253 OutboundPacket bpkt = createOutBoundBddp(portNumber);
254 context.packetService().emit(bpkt);
255 }
256 }
257
258 boolean containsPort(long portNumber) {
259 return ports.contains(portNumber);
260 }
261
262}