blob: 2671d93064aee5cb9d0edd4f8afe6e4a24cd54c2 [file] [log] [blame]
Ray Milkeyb7f0f642016-01-22 16:08:14 -08001/*
2 * Copyright 2016 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.util.HashSet;
19import java.util.Map;
20import java.util.Optional;
21import java.util.Set;
22import java.util.concurrent.ConcurrentHashMap;
23
24import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Property;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.onlab.packet.Ethernet;
31import org.onlab.packet.ONOSLLDP;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.mastership.MastershipService;
35import org.onosproject.net.ConnectPoint;
36import org.onosproject.net.DefaultAnnotations;
37import org.onosproject.net.Device;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.Link;
40import org.onosproject.net.LinkKey;
41import org.onosproject.net.Port;
42import org.onosproject.net.PortNumber;
43import org.onosproject.net.config.NetworkConfigEvent;
44import org.onosproject.net.config.NetworkConfigListener;
45import org.onosproject.net.config.NetworkConfigRegistry;
46import org.onosproject.net.config.basics.BasicLinkConfig;
47import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.device.DeviceService;
50import org.onosproject.net.flow.DefaultTrafficSelector;
51import org.onosproject.net.flow.TrafficSelector;
52import org.onosproject.net.link.DefaultLinkDescription;
53import org.onosproject.net.link.LinkProvider;
54import org.onosproject.net.link.LinkProviderRegistry;
55import org.onosproject.net.link.LinkProviderService;
56import org.onosproject.net.packet.InboundPacket;
57import org.onosproject.net.packet.PacketContext;
58import org.onosproject.net.packet.PacketPriority;
59import org.onosproject.net.packet.PacketProcessor;
60import org.onosproject.net.packet.PacketService;
61import org.onosproject.net.provider.AbstractProvider;
62import org.onosproject.net.provider.ProviderId;
63import org.slf4j.Logger;
64import org.slf4j.LoggerFactory;
65
66import static org.onlab.packet.Ethernet.TYPE_BSN;
67import static org.onlab.packet.Ethernet.TYPE_LLDP;
68import static org.onosproject.net.PortNumber.portNumber;
69
70/**
71 * Provider to pre-discover links and devices based on a specified network
72 * config.
73 */
74
75@Component(immediate = true)
76public class NetworkConfigLinksProvider
77 extends AbstractProvider
78 implements LinkProvider {
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected LinkProviderRegistry providerRegistry;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected DeviceService deviceService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected PacketService packetService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected MastershipService masterService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected NetworkConfigRegistry netCfgService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected CoreService coreService;
97
98 private static final String PROP_PROBE_RATE = "probeRate";
99 private static final int DEFAULT_PROBE_RATE = 3000;
100 @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
101 label = "LLDP and BDDP probe rate specified in millis")
102 private int probeRate = DEFAULT_PROBE_RATE;
103
104 // Device link discovery helpers.
105 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
106
107 private final DiscoveryContext context = new InternalDiscoveryContext();
108
109 private LinkProviderService providerService;
110
111 private static final String PROVIDER_NAME =
112 "org.onosproject.provider.netcfglinks";
113 private final Logger log = LoggerFactory.getLogger(getClass());
114
115 private ApplicationId appId;
116 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
117 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
118 private final InternalConfigListener cfgListener = new InternalConfigListener();
119
Ray Milkeycd6ab182016-02-03 11:13:09 -0800120 protected Set<LinkKey> configuredLinks = new HashSet<>();
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800121
122 public NetworkConfigLinksProvider() {
123 super(new ProviderId("lldp", PROVIDER_NAME));
124 }
125
126 private void createLinks() {
127 netCfgService.getSubjects(LinkKey.class)
128 .forEach(linkKey -> configuredLinks.add(linkKey));
129 }
130
131 @Activate
132 protected void activate() {
133 log.info("Activated");
134 appId = coreService.registerApplication(PROVIDER_NAME);
135 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
136 providerService = providerRegistry.register(this);
137 deviceService.addListener(deviceListener);
138 netCfgService.addListener(cfgListener);
139 requestIntercepts();
140 loadDevices();
141 createLinks();
142 }
143
144 @Deactivate
145 protected void deactivate() {
146 withdrawIntercepts();
147 providerRegistry.unregister(this);
148 disable();
149 log.info("Deactivated");
150 }
151
152 /**
153 * Loads available devices and registers their ports to be probed.
154 */
155 private void loadDevices() {
156 deviceService.getAvailableDevices()
157 .forEach(d -> updateDevice(d)
158 .ifPresent(ld -> updatePorts(ld, d.id())));
159 }
160
161 private Optional<LinkDiscovery> updateDevice(Device device) {
162 if (device == null) {
163 return Optional.empty();
164 }
165
166 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
167 did -> new LinkDiscovery(device, context));
168 if (ld.isStopped()) {
169 ld.start();
170 }
171 return Optional.of(ld);
172 }
173
174 /**
175 * Updates ports of the specified device to the specified discovery helper.
176 */
177 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
178 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
179 }
180
181
182 private void updatePort(LinkDiscovery discoverer, Port port) {
183 if (port == null) {
184 return;
185 }
186 if (port.number().isLogical()) {
187 // silently ignore logical ports
188 return;
189 }
190
191 discoverer.addPort(port);
192 }
193
194 /**
195 * Disables link discovery processing.
196 */
197 private void disable() {
198
199 providerRegistry.unregister(this);
200 discoverers.values().forEach(LinkDiscovery::stop);
201 discoverers.clear();
202
203 providerService = null;
204 }
205
206 /**
207 * Provides processing context for the device link discovery helpers.
208 */
209 private class InternalDiscoveryContext implements DiscoveryContext {
210 @Override
211 public MastershipService mastershipService() {
212 return masterService;
213 }
214
215 @Override
216 public LinkProviderService providerService() {
217 return providerService;
218 }
219
220 @Override
221 public PacketService packetService() {
222 return packetService;
223 }
224
225 @Override
226 public long probeRate() {
227 return probeRate;
228 }
229
230 @Override
231 public boolean useBddp() {
232 return true;
233 }
234
235 @Override
236 public void touchLink(LinkKey key) {
237 //linkTimes.put(key, System.currentTimeMillis());
238 }
239 }
240
241 LinkKey extractLinkKey(PacketContext packetContext) {
242 Ethernet eth = packetContext.inPacket().parsed();
243 if (eth == null) {
244 return null;
245 }
246
247 ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
248 if (onoslldp != null) {
249 PortNumber srcPort = portNumber(onoslldp.getPort());
250 PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
251 DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
252 DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
253
254 ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
255 ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
256 return LinkKey.linkKey(src, dst);
257 }
258 return null;
259 }
260
261 /**
Ray Milkeycd6ab182016-02-03 11:13:09 -0800262 * Removes after stopping discovery helper for specified device.
263 * @param deviceId device to remove
264 */
265 private void removeDevice(final DeviceId deviceId) {
266 discoverers.computeIfPresent(deviceId, (did, ld) -> {
267 ld.stop();
268 return null;
269 });
270
271 }
272
273 /**
274 * Removes a port from the specified discovery helper.
275 * @param port the port
276 */
277 private void removePort(Port port) {
278 if (port.element() instanceof Device) {
279 Device d = (Device) port.element();
280 LinkDiscovery ld = discoverers.get(d.id());
281 if (ld != null) {
282 ld.removePort(port.number());
283 }
284 } else {
285 log.warn("Attempted to remove non-Device port", port);
286 }
287 }
288
289
290 /**
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800291 * Processes incoming packets.
292 */
293 private class InternalPacketProcessor implements PacketProcessor {
294 @Override
295 public void process(PacketContext context) {
296 if (context == null || context.isHandled()) {
297 return;
298 }
299
300 Ethernet eth = context.inPacket().parsed();
301 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
302 return;
303 }
304
305 InboundPacket inPacket = context.inPacket();
306 LinkKey linkKey = extractLinkKey(context);
307 if (linkKey != null) {
308 if (configuredLinks.contains(linkKey)) {
309 log.debug("Found configured link {}", linkKey);
310 LinkDiscovery ld = discoverers.get(inPacket.receivedFrom().deviceId());
311 if (ld == null) {
312 return;
313 }
314 if (ld.handleLldp(context)) {
315 context.block();
316 }
317 } else {
318 log.debug("Found link that was not in the configuration {}", linkKey);
319 providerService.linkDetected(
320 new DefaultLinkDescription(linkKey.src(),
321 linkKey.dst(),
322 Link.Type.DIRECT,
323 DefaultLinkDescription.NOT_EXPECTED,
324 DefaultAnnotations.EMPTY));
325 }
326 }
327 }
328 }
329
330 /**
331 * Requests packet intercepts.
332 */
333 private void requestIntercepts() {
334 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
335 selector.matchEthType(TYPE_LLDP);
336 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
337 appId, Optional.empty());
338
339 selector.matchEthType(TYPE_BSN);
340
341 packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
342 appId, Optional.empty());
343
344 }
345
346 /**
347 * Withdraws packet intercepts.
348 */
349 private void withdrawIntercepts() {
350 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
351 selector.matchEthType(TYPE_LLDP);
352 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
353 appId, Optional.empty());
354 selector.matchEthType(TYPE_BSN);
355 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL,
356 appId, Optional.empty());
357 }
358
359 /**
360 * Processes device events.
361 */
362 private class InternalDeviceListener implements DeviceListener {
363 @Override
364 public void event(DeviceEvent event) {
365 if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) {
366 return;
367 }
368 Device device = event.subject();
369 Port port = event.port();
370 if (device == null) {
371 log.error("Device is null.");
372 return;
373 }
374 log.trace("{} {} {}", event.type(), event.subject(), event);
375 final DeviceId deviceId = device.id();
376 switch (event.type()) {
377 case DEVICE_ADDED:
378 case DEVICE_UPDATED:
379 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
380 break;
381 case PORT_ADDED:
382 case PORT_UPDATED:
383 if (port.isEnabled()) {
384 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
385 } else {
386 log.debug("Port down {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800387 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800388 providerService.linksVanished(new ConnectPoint(port.element().id(),
389 port.number()));
390 }
391 break;
392 case PORT_REMOVED:
393 log.debug("Port removed {}", port);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800394 removePort(port);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800395 providerService.linksVanished(new ConnectPoint(port.element().id(),
396 port.number()));
397 break;
398 case DEVICE_REMOVED:
399 case DEVICE_SUSPENDED:
400 log.debug("Device removed {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800401 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800402 providerService.linksVanished(deviceId);
403 break;
404 case DEVICE_AVAILABILITY_CHANGED:
405 if (deviceService.isAvailable(deviceId)) {
406 log.debug("Device up {}", deviceId);
407 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
408 } else {
409 log.debug("Device down {}", deviceId);
Ray Milkeycd6ab182016-02-03 11:13:09 -0800410 removeDevice(deviceId);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800411 providerService.linksVanished(deviceId);
412 }
413 break;
414 case PORT_STATS_UPDATED:
415 break;
416 default:
417 log.debug("Unknown event {}", event);
418 }
419 }
420 }
421
422 private class InternalConfigListener implements NetworkConfigListener {
423
424 private void addLink(LinkKey linkKey) {
425 configuredLinks.add(linkKey);
426 }
427
428 private void removeLink(LinkKey linkKey) {
429 DefaultLinkDescription linkDescription =
430 new DefaultLinkDescription(linkKey.src(), linkKey.dst(),
431 Link.Type.DIRECT);
432 configuredLinks.remove(linkKey);
433 providerService.linkVanished(linkDescription);
434 }
435
436 @Override
437 public void event(NetworkConfigEvent event) {
438
439 if (event.configClass().equals(BasicLinkConfig.class)) {
440 log.info("net config event of type {} for basic link {}",
441 event.type(), event.subject());
442 LinkKey linkKey = (LinkKey) event.subject();
443 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
444 addLink(linkKey);
445 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
446 removeLink(linkKey);
447 }
448 }
449 log.info("Reconfigured");
450 }
451 }
452
453}